typescriptexpresscommonjsnodejs-serverinversifyjs

Get Async Dependency globally Inversify


I wish to get an asynchronous dependency at the top level, without having to use top level awaits.

Currently I am using a temporary hack by declaring getService(), an asynchronous function, in the controller file. However, by doing so I have to call the getService() function for every route that I declare in my controller file.

Here is my current code:

// something.controller.ts

const router = Router();

async function getService() {  // temporary hack
    return await container.getAsync<IService>(TYPES.Service)
}


router.get("/collection",
    paginateCollectionSchema,
    validateRequestSchema,

    async (req, res) => {
        const service = await getService(); // if I have 100 routes, I have do this 100 times
        const paginationSettings = await service.getSome(req.query.limit, req.query.offset);
        const pagination = paginate("/collection", paginationSettings);

        return res.json(pagination)
    },
);


...

export router;

What I hope to achieve is something like this:

// something.controller.ts


const router = Router();

// get service once without using top level await


router.get("/collection",
    paginateCollectionSchema,
    validateRequestSchema,

    async (req, res) => {
        // no need to get service
        const paginationSettings = await service.getSome(req.query.limit, req.query.offset);
        const pagination = paginate("/collection", paginationSettings);

        return res.json(pagination)
    },
);


...

export router;

Solution

  • If you have some asynchronous initialization that you would like to complete before your server runs so you can use that asynchronous result as a regular variable in the rest of your server, then you can make that asynchronous call and then don't start your server until the result is available. Then, you can just put it in a variable and reference it as a regular variable from any of the server request handlers.

    If you need that value in some other module, then you can make a synchronous getter function that you export that will return the variable (you can't directly export it).

    FYI, this type of problem is exactly why top level await was invented to make the solution a lot simpler.

    You don't show very much of the structure of your code, but here's the general idea:

    // variable at top scope you can use in the rest of the code AFTER
    // the server has been started
    let mainService;
    
    getService().then(service => {
        mainService = service;
        // don't start your server until you have the service available
        app.listen(...);
    }).catch(err => {
        console.log("Failed to start server", err);
    });
    
    app.get((req, res) => {
        // can use mainService variable directly here
    });