cyberwombat
cyberwombat

Reputation: 40133

Express middleware next issue

I am not clear on how to sequentially call Express middleware. I would like to have the next middleware in line only happen once the previous one is done. I thought that I had to call next() for it to happen but evidently that is not the case.

mongoose.connect(app.set("db-uri"));

app.use(function(req, res, next) {
 if(1 !== mongoose.connection.readyState) {
    console.log('Database not connected');
    res.render("system/maintenance", {
      status: 500
    });
  } else {
    return next();
  }
});


// This middleware does not wait for the previous next(). It just tries to connect before actually.
app.use(express.session({
  secret: settings.sessionSecret,
  maxAge: new Date(Date.now() + 3600000),
  store: new MongoStore({ mongoose_connection: mongoose.connections[0], auto_reconnect: true })
}));

Edit: Updated code to only connect Mongoose on start and check state in middleware

Upvotes: 0

Views: 681

Answers (2)

cyberwombat
cyberwombat

Reputation: 40133

Ok this seems to work. Open to comments on issues here.

var store;
mongoose.connect(app.set("db-uri"));
mongoose.connection.on("connect", function(err) {
  // Create session store connection
  store = new MongoStore({ mongoose_connection: mongoose.connections[0], auto_reconnect: true });
});


app.use(function(req, res, next) {
  if(1 !== mongoose.connection.readyState) {
    console.log('Database not connected');
    // Try a restart else it will stay down even when db comes back up
    mongoose.connect(app.set("db-uri"));
    res.render("system/maintenance", {
      status: 500
    });
  } else {
    return next();
  }
});

app.use(express.session({
  secret: settings.sessionSecret,
  maxAge: new Date(Date.now() + 3600000),
  store: store
}));

Appears to work - I was concerned session store would not come back up but I think it works.

Upvotes: 0

Andreas Hultgren
Andreas Hultgren

Reputation: 14953

I admit this isn't a very complete answer, not to mention tested, but the gist of it should work.

First of all, connect to the database when starting the app, eg not in a middleware. Just put the moongoose.connect stuff outside of app.use somewhere (see any mongoose+express example, like this one).

Second, I'd use a "flag" that keeps track of whether or not mongoose has disconnected.

var mongooseIsConnected = false;

mongoose.on('open', function () {
  mongooseIsConnected = true;
});

mongoose.on('error', function () {
  mongooseIsConnected = false;
});

Note that this is very much a guess. I don't know if the error event fires only on connection failure and likewise I don't know if "open" fires when reconnecting. If you find that in some docs please tell me and I'll update this answer.

Finally it's straight forward to put a middleware – before any other middlewares using the database – which checks if the flag is true or false and either passes the request on or renders an error.

app.use(function (req, res, next) {
  if(mongooseIsConnected) {
    next();
  }
  else {
    res.status(500);
    res.render('errorView');
    // Or you could call next(new Error('The database is broken')); and handle 
    // the error in a central express errorHandler
  }
});

// app.use(express.session etc here...

Update, a solution to not use the mongoose connection before it's open:

mongoose.connect('uri...', configureApp);

function configureApp () {
  app.use(express.session({
    secret: settings.sessionSecret,
    maxAge: new Date(Date.now() + 3600000),
    store: new MongoStore({ mongoose_connection: mongoose.connections[0], auto_reconnect: true })
  }));

  // The other middleware and app.listen etc
}

This should ensure the connection is made before the middlewares are defined. However when digging though my own code I see that I simply let MongoStore create a new connection. That's probably easier.

Upvotes: 1

Related Questions