Using Middleware with Next.js

Why Middleware?

Next.js API routes are thin and fast out-of-the-box, but they sometimes lack features that we need in production. Middleware can act as a layer between a request and the route file to further extend its functionality.

Common use cases

  • Authentication
  • Enabling CORS
  • Security Checkpoints
  • Request Validation
  • and many more

How it works

Middleware is just a function that wraps another function by passing through the req and res. Inside middleware, you can extend the functionality of your request before it runs the code inside your route file.

Example

In this example, the Middleware function is exported from lib/middleware.js. This simple example is setup to apply security headers using the micro-helmet which simply wraps the helmet security NPM library. Learn more about helmet here.

// lib/middleware.js
import helmet from "micro-helmet";

export default Middleware (handler) => async (req, res) => {
    try {
        // apply a blanket of security headers
        await helmet.addHeaders(req, res);

        // other custom code here

        // pass this onto the next route
        return handler(req, res);
    }
    catch (ex) {
        res.status(400).json({ error: "Something went boom" })
    }
}

Instead of exporting default async function (req, res) => {}, we wrap Middleware around the route. The API route file would look something like this:

// pages/api/posts.js
export default Middleware(async (req, res) => {
  // this route will have all the middleware methods applied
  // in this example, security headers
  res.json()
})

Things to remember

  1. Keep middleware lightweight and fast.
  2. Middleware can wrap other middleware. In some cases, it may be helpful to create a Database middleware and a separate Authentication middleware.