Express.js: Passing Variables Through Middleware

Updated: December 28, 2023 By: Guest Contributor Post a comment

Middleware functions are a fundamental part of Express.js, allowing developers to perform operations on the request and response objects, and end the request-response cycle or call the next middleware in the stack. One common need is to pass data between middleware functions, enabling a sort of ‘conversation’ or ‘context’ to be built up during a request’s lifecycle. In this tutorial, we’ll explore several ways to pass variables through middleware in an Express.js application.

Using req object

One of the simplest ways to pass data between middleware functions is through the req object, which represents the HTTP request. Middleware can add properties to this object to be accessed by subsequent middleware.

const express = require('express');
const app = express();

function logger(req, res, next) {
  req.requestTime = Date.now();
  next();
}

app.use(logger);

app.get('/', (req, res) => {
  res.send(`Request time: ${req.requestTime}`);
});

app.listen(3000);

Using response locals

Another way to pass data is with res.locals, an object that contains response local variables scoped to the request. This is useful for passing data to templates rendered by the response.

app.use((req, res, next) => {
  res.locals.requestTime = Date.now();
  next();
});

app.get('/', (req, res) => {
  res.render('index', { requestTime: res.locals.requestTime });
});

Passing Object References

Instead of passing individual variables, it’s often useful to pass an entire object reference. This can be done similarly to passing primitives, but it allows for a more structured and potentially encapsulated way of sharing data.

function attachData(req, res, next) {
  req.context = { requestTime: Date.now() };
  next();
}

app.use(attachData);

app.get('/', (req, res) => {
  res.send(`Request time: ${req.context.requestTime}`);
});

Error Handling Middleware

We can also pass variables through error handling middleware functions. This can help to provide more context when something goes wrong.

function errorHandler(err, req, res, next) {
  console.error(err.stack);
  req.errorContext = { error: 'InternalServerError', time: Date.now() };
  next(err);
}

function logError(req, res, next) {
  const errorContext = req.errorContext;
  console.error(errorContext);
  next();
}

app.use(errorHandler);
app.use(logError);

Advanced Scenarios

For more advanced scenarios, such as ensuring certain middleware runs before others or when using conditional middleware, passing variables can become more complex.

Here is an example using conditional middleware and closures to encapsulate the variable passing.

function conditionallyAttachData(condition) {
  return function(req, res, next) {
    if (condition(req)) {
      req.context = {...req.context, specificKey: 'someValue'};
    }
    next();
  };
}

app.use(conditionallyAttachData(req => req.path === '/special'));

Conclusion

In this tutorial, we have looked at various methods to pass variables through middleware in Express.js. While the req object and res.locals are often sufficient for many use cases, understanding how to pass entire object references and use advanced patterns can greatly enhance the flexibility and maintainability of your application.

Implementing these techniques will enable better communication between different parts of your middleware stack, helping you to build more modular and robust web applications.