Search code examples
node.jstypescriptexpressinversion-of-controlexpress-router

Using Service in Express Router


I am pretty new in the NodeJS but I would like to learn something new. I came from .NET fancy dependency injection, inversion of controll, microservice shiny world so I am trying write some service in TypeScript based on my previous experiences. I am using express and express router to create some api. I have some methods in router which handles api calls and I want to use some kind of service object for data retrieving and manipulation. I inject the service into the router using constructor injection but if I want to use my service it throws an error:

TypeError: Cannot read property 'layoutService' of undefined

I understood that the methods were called withouth context so I added .bind(this) to the each method regsitration and it works, but I dont know if it is the best way how to do it.

Does anyone have a better idea?

simplified server.ts

import express, { Router } from "express";

// inversion of controll
import container from "./ioc";
import { TYPE } from "./constants";

import IMyService from "./abstract/IMyService";

// import routers
import MyRouter from "./api/MyRouter";

app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

const router: Router = express.Router();
const myRouter: MyRouter = new MyRouter(container.get<IMyService>(TYPE.IMyService));

app.use("/", router);
app.use("/api/v1/layouts", layoutRouter.router);

MyRouter.ts

import IMyService from "./abstract/IMyService";
import { Router, Request, Response } from "express";
import { inject } from "inversify";
import { TYPE } from "../constants";

export default class MyRouter {
    public readonly router: Router;
    private readonly myService: IMyService;

    constructor(
        @inject(TYPE.IMyService) myService: IMyService
    ) {
        this.myService = myService;
        this.router = Router();
        this.routes();
    }

    public GetAll(req: Request, res: Response): void {
        this.myService.getAll()
            .then(data => {
                const status: number = res.statusCode;

                res.json({ status, data });
            })
            .catch(err => {
                const status: number = res.statusCode;

                res.json({ status, err });
            });
    }   

    public GetOne(req: Request, res: Response): void {
        const id: string = req.params.id;

        this.myService.getOne(new ObjectID(id))
            .then(data => {
                const status: number = res.statusCode;

                res.json({ status, data });
            })
            .catch(err => {
                const status: number = res.statusCode;

                res.json({ status, err });
            });
    }

    routes(): void {
        this.router
            .get("/", this.GetAll)
            .get("/:id", this.GetOne);
    }
}

Solution

  • If you define your function with the arrow syntax (ES6), it will "bind" the context to it automatically and you won't need to bind them. But it will depends on your use case (ou might need to bind a different context)