Use middleware in Next.js without custom server
Tags: nextjs, react, middleware, javascript
Published: 2019-10-19 16:37:26

Three months ago, Next.js released version 9, which added [API Routes](https://www.hoangvvo.com/blog/explore-next-js-9-api-routes/ "API Routes"). This enabled us to write... well... APIs by exporting functions of two arguments `req` and `res`, which are extensions to Node's [http.ClientRequest](https://nodejs.org/api/http.html#http_class_http_clientrequest "Client request") and [http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse "Server response").

This was a good excuse to move away from frameworks like Express as a [custom server](https://nextjs.org/docs#custom-server-and-routing).

Still, something was missing: Middleware.

## Middleware

Lots of us have probably learned the concept of middlewares when we worked with Express or Connect. The concept allowed us to augmented `req` and `res` by routing them through layers of a stack, which are known as `middleware`.

The usage is somewhat like below:

```javascript
app.use((req, res, next) => {
  //  Augmenting req
  req.user = getUser(req);
  //  Go to the next layer
  next();
});
```

More often, we find ourselves using libraries:

```javascript
app.use(passport.initialize());
```

In those cases, the libraries actually return functions of `(req, res, next)` just like the way we approached above.

However, in Next.js API Routes, we do not have such ability. We can only export a function of `(req, res)`, there is no interfaces of `app.use()`. This limitation led people back to using Express, thus rendering API Routes useless.

## Possible solutions

Luckily, there are ways to achieve the similar behavior that is in Express.

Let's write some middles.

![](https://media.giphy.com/media/fH6fIORFYdaKd6xtWX/giphy.gif)

**If you are unsure of which approach to go with, I recommend my [next-connect](#middleware-with-next-connect).**

### Wrappers around handler function

I will define *handler function* as the function of `(req, res)` that we need to export for API Routes.

#### Concept illustration in React

To illustrate the concept, I might use the term `Higher-order component` (HOC) from [React](https://reactjs.org/docs/higher-order-components.html "Higher-order components") (even though it is a little bit misleading). If you use React, you may know the technique as something like the one below:

```javascript
const EnhancedComponent = higherOrderComponent(WrappedComponent);
```

`higherOrderComponent` will be something like below:

```jsx
function higherOrderComponent(OriginalComponent) {
  const user = getUser();
  return (<OriginalComponent user={user} />);
}
```

In the above example, we **wrap** `WrappedComponent` with `higherOrderComponent`. Like the variable name suggests, it returned an enhanced version of the original component, not a different one. The only difference was that it added a prop to the original component. The HOC **augmented** the original component, not **mutate** it.

We can apply the concept into making a "HOC" for API Routes.

#### Making a middleware as a wrapper

I will take an (simplified) example from my project [nextjs-mongodb-app](https://github.com/hoangvvo/nextjs-mongodb-app/). (check it out too)

```javascript
const withDatabase = handler => {
  return async (req, res) => {
    await client.connect();
    req.db = client.db('somedb');
    return handler(req, res);
  };
}

export default withDatabase;
```

Looking at the function `withDatabase`, it accepts an argument called `handler`, our original function. `withDatabase` actually returns a function of `(req, res)` (`return async (req, res)`), which will accept the incoming requests. We can say that it replaces the original `(req, res)` at this point.

Looking at the part:

```javascript
req.db = client.db('somedb');
```

The incoming request (the original `req` object) does not have `db`, and we are adding it. Particularly, we are assigning `db` into `req` so we can access it later.

Now that we have augmented `req`, we want to route it through our original `handler`. Looking at `return handler(req, res);`, we are calling the original `handler` function we retrieve as an argument with the augmented `req` and (eh, unchanged) `res`.

Now in my original `handler`, I can use the pass-along `db`.

```javascript
const handler = async (req, res) => {
  const user = await req.db.findOne({ userName: req.body.username });
  res.send(`Our homie is ${user.name}`);
}
```

Remember that `withDatabase` needs `handler`. We simply do `withDatabase(handler)`. We now `export` like so:

```javascript
import withDatabase from '../middleware/withDatabase';

const handler = async (req, res) => {
  const user = await req.db.findOne({ userName: req.body.username });
  res.send(`Our homie is ${user.name}`);
}

export default withDatabase(handler);
//  instead of export default handler;
```

What about an additional option? Let's say I want to specify the database to use. We can simply add it as the second argument. Let's rewrite our `withDatabase`.

```javascript
const withDatabase = (handler, dbToUse) => {
  return async (req, res) => {
    await client.connect();
    req.db = client.db(dbToUse);
    return handler(req, res);
  };
}
```

Now back to our API Route file:

```javascript
export default withDatabase(handler, 'testingDb');
```

Obviously, you can add as many arguments as you want, we only need to make sure to pass along our original `handler`. You can look at another example at how I have `options` in [next-session](https://github.com/hoangvvo/next-session#using-withsession "withSession next session").

#### Multiple middlewares

![](https://media.giphy.com/media/l0MYtz4kg0H9nJfFK/giphy.gif)

What about multiple middlewares? We can write similar functions to `useDatabase`. Let's say we want a middleware to check the database's readiness.

```javascript
const withCheckDb = (handler) {
  return async (req, res) => {
    req.dbHealth = await checkDatabase(req.db);
    return handler(req, res);
  }
}
```

Now that we have our additional `withCheckDb`, we can wrap it along with `withDatabase`.

```javascript
export default withDatabase(withCheckDb(handler), 'testingDb');
```

One thing to be aware of is that `withCheckDb` is inside `withDatabase`. Why?

Looking at `withCheckDb`, we see that it tries to access `req.db`, which is only available after `withDatabase`. The function on the outside will receive `req` and `res` first, and only when they are done that they pass them along into the inside ones.

So, **order matters**.

#### Stop the middleware chain early

Let's take another look at our `withCheckDb`. What would happen if our database is not working? In such a case, I want it to simply respond with `Database is not working`, ideally with a **500** status code.

```javascript
const withCheckDb = (handler) {
  return async (req, res) => {
    req.dbHealth = await checkDatabase(req.db);
    if (req.dbHealth === 'bad') return res.status(500).send('Database is not working :( so sorry! ');
    return handler(req, res);
  }
}
```

If the result of our `checkDatabase` is bad, we send the message "Database is not working". More importantly, we also `return` at that point, exiting the function. `return handler(req, res);` is not executed because the function has existed/returned earlier.

By doing so, the actual `handler` never run, thus the chain is cut short.

### Mutate req and res directly

Another approach to middleware is to manipulate `req` and `res` directly. We can try to rewrite the above functions `withDatabase` and `withCheckDb` using this approach.

```javascript
const useDatabase = async (req, res, dbToUse) => {
  await client.connect();
  req.db = client.db(dbToUse);
}
```

Instead of getting a `handler`, we instead take `req` and `res` as arguments. Actually, we do not even need `res` because we do not mutate it.

```javascript
const useDatabase = async (req, dbToUse) => {
  await client.connect();
  req.db = client.db(dbToUse);
}
```

Let's go back to our handler.


```javascript
import useDatabase from '../middleware/useDatabase';

const handler = async (req, res) => {
  await useDatabase(req, 'testingDb');
  const user = await req.db.findOne({ userName: req.body.username });
  res.send(`Our homie is ${user.name}`);
}

export default handler;
```

By calling `await useDatabase(req, 'testingDb');`, we mutate our `req` by injecting our `db` into it. I need to use `await` because we need to wait for `client.connect()`, followed by setting `req.db`. 

![](https://media.giphy.com/media/25QbQfG72lqiXD2Dpm/giphy.gif)

Without `await`, the code will go on without `req.db` and end up with a *TypeError* `req.db is not defined`.

#### Multiple middleware

Let's do the same thing with `withCheckDb`:

```javascript
const useCheckDb = async (req, res) {
  req.dbHealth = await checkDatabase(req.db);
  if (req.dbHealth === 'bad') return res.status(500).send('Database is not working :( so sorry! ');
}
```

We need `res` in this case since we call calling `res.send`.

We can then go on to use multiple middlewares like so:

```javascript
import useDatabase from '../middleware/useDatabase';
import useCheckDb from '../middleware/useCheckDb';

const handler = async (req, res) => {
  await useDatabase(req, 'testingDb');
  await useCheckDb(req, res);
  const user = await req.db.findOne({ userName: req.body.username });
  res.send(`Our homie is ${user.name}`);
}

export default handler;
```

#### Stop the middleware chain early

Remember that we want to stop the code if the database is not working. However, it does not just work with this approach.

`useCheckDb` will still call `res.status(500).send('Database is not working :( so sorry! ')`, but then the code go on. Chances are that the code will throw at `req.db.findOne({ userName: req.body.username })`, or you will end up with `Can't set headers after they are sent to the client` when you try to ```res.send(`Our homie is ${user.name}`)```.

One way is to intentionally throw an error inside `useCheckDb`

```javascript
const useCheckDb = async (req, res) {
  req.dbHealth = await checkDatabase(req.db);
  if (req.dbHealth === 'bad') throw new Error('Database is not working :( so sorry! ');
}
```

...and catch it with a Try/Catch.

```javascript
import useDatabase from '../middleware/useDatabase';
import useCheckDb from '../middleware/useCheckDb';

const handler = async (req, res) => {
  try {
    await useDatabase(req, 'testingDb');
    await useCheckDb(req, res);
    const user = await req.db.findOne({ userName: req.body.username });
    res.send(`Our homie is ${user.name}`);
  } catch (e) {
    res.status(500).send(e.message);
  }
}

export default handler;
```

`e.message`, in this case, will be "Database is not working :( so sorry!".

## Middleware with next-connect

The two above approaches did not settle me, so I decided to write a library that will take me back to good ol' Express.js.

You can get it [here](https://www.npmjs.com/package/next-connect).

**With `next-connect`, we can now use Express middleware syntax like we used to be.**

![](https://media.giphy.com/media/9MJ7eHnB9R7HmUDq0B/giphy.gif)

```javascript
import nextConnect from 'next-connect'
const handler = nextConnect();

handler.use(function (req, res, next) {
    //  Do some stuff with req and res here
    req.user = getUser(req);
    //  Call next() to proceed to the next middleware in the chain
    next();
})
 
handler.use(function (req, res) {
    if (req.user) res.end(`The user is ${req.user.name}`);
    else res.end('There is no user');
    //  next() is not called, the chain is terminated.
})
 
//  You can use a library too.
handler.use(passport.initialize());

export default handler;
```

### Method routing, too

What even better is that `next-connect` also takes care of method handling. For example, you may want `POST` request to be responded differently to `PUT` request.

```javascript
handler.post((req, res) => {
  //  Do whatever your lil' heart desires
});

handler.put((req, res) => {
  //  Do whatever your lil' heart desires
});

export default handler;
```

### Example usage with `next-connect`

Anyway, let's get back on track. Let's try to replicate `use/withDatabase` and `use/withCheckDb`.

```javascript
function database(dbToUse) {
  return async (req, res, next) => {
    await client.connect();
    req.db = client.db(dbToUse);
    //  Calling next() and moving on!
    next();
  }
}

function checkDb() {
  return async (req, res, next) => {
    req.dbHealth = await checkDatabase(req.db);
    if (req.dbHealth === 'bad') return res.status(500).send('Database is not working :( so sorry! ');
    next();
  }
}
```

The writing of the two functions are similar to our first approach. The only differences are that:

- We do not need to take in a `handler` argument
- Our returned function has an additional `next` argument.
- We finish by calling `next()` instead of calling `handler`.


What about suspending the code if `checkDb` fail? Similarly to the first approach, `next()` will not be called and whatever comes after it will not run.

For instruction on writing middlewares, here is a [guide](https://expressjs.com/en/guide/writing-middleware.html) on expressjs.com.

Now, we can use it like we do in the old days of Express.js.

```javascript
import nextConnect from 'next-connect'
import database from '../middleware/database';
import checkDb from '../middleware/checkDb';

const handler = nextConnect();

handler.use(database());
handler.use(checkDb());
handler.get((req, res) => {
    const user = await req.db.findOne({ userName: req.body.username });
    res.send(`Our homie is ${user.name}`);
});

export default handler;
```

## What about non-API pages

We have been talking about API Routes (those in `pages/api`), what about normal pages (those in `pages/`). We can apply [approach 2](#mutate-req-and-res-directly) to `getInitialProps`.

```javascript
Page.getInitialProps = async ({ req, res }) => {
  await useMiddleware(req, res);
  /* ... */
}
```

### Document middleware

An RFC in Next.js issue [#7208](https://github.com/zeit/next.js/issues/7208) enables the same approach as above but allowing it to be available globally.

It is an experimental feature and need to be enabled in `nextjs.config.js`:

```javascript
module.exports = {
  experimental: {
    documentMiddleware: true
  }
};
```

Then, in `_document.js`:

```javascript
export const middleware = async ({ req, res }) => {
  await useMiddleware(req, res);
};
```

### Using next-connect

See [this](https://github.com/hoangvvo/next-connect#document-middleware-and-getinitialprops).

## Conclusion

I hope this will help your effort on moving away from `Express.js`. Moving away from `Express.js` will allow our app to run faster by enabling `Next.js`'s optimization (and serverless, too!).

If you have any questions, feel free to leave a comment. I also recommend asking on [Next.js channel on Spectrum](https://spectrum.chat/next-js) to get answers from great people there.

Good luck on your *next* Next.js project!