0

My first goal is to avoid repeating myself. I am creating a Node.js express server. I want to create several class controllers each with their own route, and all with the exact same CRUD functionality: GET a single record, GET all the records, UPDATE a single record, DELETE a record, POST a new record for each data object in my database, but I want to be able to extend these controllers to add additional functionality on top of these.

My second goal is to use dependency injection to use a database service in these controllers.

The problems are the TypeScript compiler gets upset when I inject it in the base class constructor. It now wants me to add it to the subclass constructor,


// THE BASE CLASS
import { inject } from "inversify";
import db from "../db";

export default class Controller {

    protected _db: db;
    public path: string;
    public router = Router();

    constructor(path: string, @inject(db) databbase: db) {
        this._db = databbase;
        this.path = path; // path for my special record
        this.initializeRoutes();
    }

    public initializeRoutes(): void {
        this.router.get(this.path + '/:id', this.getRecordById);
    }

    getRecordById = async (req: Request, res: Response): Promise<boolean>  => {
        const { rows } = await this._db.query('SELECT * FROM issues WHERE id = $1', [req.params.id]);
      
        res.send(rows.pop());
        return Promise.resolve(true);
    }
}
// THE SUBCLASS
import { inject } from "inversify";
import db from "../db";
import Controller from "./Controller";


export default class SubController extends Controller {

    constructor(path: string, @inject(db) _db: db) { // <-- Do I have to inject it here, as well?
        super(path, _db);
    }

    // I will add additional methods here, unique to my SubController

}

then when I need to use that class, it now wants me to fill in the second argument, the db part of the sub-class constructor.

See, now when I need to use that class it wants me to put something in the second parameter for the constructor.

In the documentation they give an example which implies I don't even need to use the @inject keyword, but that doesn't make sense to me. Ultimately, I have to put something in that constructor, don't I? When I finally go new IssueController('/path', [@inject db something here]), won't I need to put something in where @inject is?

My Questions

  1. Do I need to use @inject in both the base class and the sub-class?
  2. What do I insert when I need to call new?

Ultimately, it seems like I'm doing this wrong. Can you point me in the right direction?

Gabriel Kunkel
  • 2,643
  • 5
  • 25
  • 47
  • @EstusFlask I understand. I will try this. I think this DI stuff is just blowing my mind at the moment. – Gabriel Kunkel Nov 01 '20 at 00:11
  • The point of using an injector is that you don't instantiate classes manually but allow an injector to do that. In your case you may not need Inversify at all. You can likely omit a dep in child class, but only if you omit a constructor (it's inherited from a parent). There shouldn't be things like "path: string" in DI classes, they should receive only valid DI deps in constructor Yes, @ inject is unnecessary if you use @ injectable. – Estus Flask Nov 01 '20 at 00:13
  • @EstusFlask So, a class that uses a injected dependency cannot otherwise take in ANY other values into the constructor? ...So, then nearly everything is a service in an application that uses dependency injection. Hmmmm. – Gabriel Kunkel Nov 01 '20 at 00:18
  • I'm having the "ah-ha" moment. Thanks, Estus. – Gabriel Kunkel Nov 01 '20 at 00:21
  • You'll need another service (factory function) to provide a parameter to a class. In your case you could use dependency injection pattern without Inversify container by doing "new IssueController("issues", dbForIssue)", and this does the trick most times. I can hardly justify the use of DI container in small to medium-size Node app. This results in overengineered app without obvious benefits. You can check how DI looks with Express in Nestjs in practice. It's bulky and enterprisey. – Estus Flask Nov 01 '20 at 00:23
  • @EstusFlask That makes sense... This is largely for educational benefit. :-) – Gabriel Kunkel Nov 01 '20 at 01:08

0 Answers0