Express Middleware

Aug 29, 2019

When an Express server receives an HTTP request, it executes a list of middleware functions. The middleware functions are responsible for handling the request and crafting a response.

You will usually see middleware defined as a function with 3 parameters: req, res, and next. The biggest exception to this rule is error handling middleware. To add a middleware function to your Express app, you call app.use().

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

app.use((req, res, next) => {
  req; // The request
  res; // The response
  next; // A function that you must call to trigger the next middleware
});

Under the hood, when you call app.use(), Express adds your function to its internal middleware stack. Express executes middleware in the order they're added, so if you call app.use(fn1); app.use(fn2);, Express will execute fn1 before fn2.

Middleware vs Route Handlers

Suppose you have a simple Express server that responds to GET requests with the string 'Hello, World' as shown below.

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

app.get('/', function routeHandler(req, res) {
  res.send('Hello, World');
});

In Express, a route handler is just a special type of middleware that never calls next(). You could also write a middleware that does the same thing.

app.use(function(req, res, next) {
  // Do nothing if the request isn't `GET /`
  if (req.method !== 'GET' || req.url !== '/') {
    return next();
  }
  res.send('Hello, World');
});

Routing

The app.use() function takes 2 parameters: an optional path, and a middleware function callback. If the first parameter to app.use() is a string, Express will only execute the corresponding middleware function if the URL matches.

// Express will only call `middleware()` if `req.url` is equal to '/'
app.use('/', function middleware(req, res, next) {
  // Do nothing if the request isn't a 'GET' request
  if (req.method !== 'GET') {
    return next();
  }
  res.send('Hello, World');
});

The next() Function

If you have multiple middleware functions, you need to make sure your middleware either calls next() or send a response. If you write Express middleware, this is your responsibility. Express will not throw an error if your middleware doesn't call next(), it will simply hang.

// If you open this page in Chrome, it will just keep loading forever.
app.use('/', function middleware(req, res, next) {
  console.log('Test');
});

In general, it is best practice to call next() unless you explicitly do not want the rest of the middleware stack to run. Calling next() if there's no more middleware is perfectly fine.

// It is OK to call `next()` even if there's no more middleware.
app.use((req, res, next) => {
  res.send('Hello, World');
  next();
});

If you call next() with a parameter, Express will treat that parameter as an error and trigger error handling middleware. The below middleware will cause Express to respond with an HTTP 500 and a stack trace.

app.use((req, res, next) => {
  next(new Error('Fail!'));
});

If you open the above middleware in Chrome, you'll see something like this:

An example of an HTTP response error
Want to become your team's Express expert? There's no better way to really grok a framework than to write your own clone from scratch. In 15 concise pages, this tutorial walks you through how to write a simplified clone of Express called Espresso. Get your copy!

Espresso supports:
  • Route handlers, like `app.get()` and `app.post()`
  • Express-compatible middleware, like `app.use(require('cors')())`
  • Express 4.0 style subrouters
As a bonus, Espresso also supports async functions, unlike Express.

Get the tutorial and master Express today!

Did you find this tutorial useful? Say thanks by starring our repo on GitHub!

More Express Tutorials