giodamelio
giodamelio

Reputation: 5605

Express middleware with async initialization

I am writing some express middleware that needs to access a database. It is going to be released as a package so I want it to be as self contained as possible. I was wondering how I should handle the connection to the database. It is async(of course), but it only needs to happen once when the package is initialized. Where should this happen?

I was thinking something like this. The problems is, the middleware is passed back right away, before the database is ready.

// App
app.use(myMiddleware({
    db: "<db connection string>"
});

// Middleware
module.exports = function(db) {
    // Open db

    return function(req, res, next) {
        // Middleware stuff
    }
}

Upvotes: 3

Views: 2481

Answers (2)

anniex
anniex

Reputation: 356

use an async function to wrap your create app code and then app.listen after everything is initialized.

// app.js
import express from "express";

export default async () => {
  const app = express();
  
  const asyncMiddleware) = await initMWAsync();
  app.use(asyncMiddleware);
  
  return app;
}

// entry point of your program
import createApp from "./app";

const server = createApp()
  .then(app => app.listen(app.get("port"), () => {
    console.log(
      "  App is running at http://localhost:%d in %s mode",
      app.get("port"),
      app.get("env")
    );
    console.log("  Press CTRL-C to stop\n");
  }));

export default server;

Upvotes: 0

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276306

I'd recommend against such a singleton, dependency injection is a better solution here, and a connection per app is hardly scalable. A connection pool might be a better idea.

That said, you can do something like:

var db = null; // to keep our instance
var asked = false; // to not make several requests that override each other
function openDb(db,ready){
    // if we already have it, we return the instance
    if(db !== null) ready(null,db);
    // if it was already asked by another instance, keep track of it
    // to avoid multiple requests.         
    if(asked) asked.push(ready);
    asked = [];
    openDbCode(db,function(err,result){
        if(err) {
           ready(err,null); // error case
           asked.forEach(function(fn){ fn(err,null); }); // notify waiters of failure
        }
        db = result; // save the reference
        asked.forEach(function(fn){ fn(db,null); }); // notify all waiters
    })
}

This function effectively waits for a db for the first asker and then calls everyone on the same instance. Note that this function will use the first connection string provided.

module.exports = function(db) {
    return function(req, res, next) {
       openDb(db,function(err,db){
           if(err) handleDbOpenErrorLogicHere();
           // middleware stuff, same db available here, call next to continue
        });
    };
}

Upvotes: 2

Related Questions