Middleware in NextJS 12 - What are they and how to get started with them
Yesterday, we had the NextJS Conf and we got a lot of new things. NextJS 12 has been released and it has got a ton of new and exciting features. One of these features is middleware in NextJS so let us see how it works with an example.
What is NextJS Middleware?
Middleware are simple pieces of code that allows one to modify the response to a request even before it is completed. We can rewrite, redirect, add headers or even stream HTML based on the user's request.
This definition is from the NextJS Middleware Docs
Getting Started with NextJS 12
I am going to do everything in Replit for this example. You can also follow along with me on this tutorial.
The Repl - replit.com/@AnishDe12020/NextJS-12-Middleware
Creating a NextJS project with create-next-app
will give us a project with NextJS 11 set up (as of 27th October 2021) so, firstly, we need to upgrade to NextJS 12.
With NPM -
npm install next@latest
With Yarn -
yarn add next@latest
A simple middleware
Now, to add middleware, we need to make a _middleware.js
file in our pages directory. Note that you can also add middleware files in sub-directories in the pages directory to make it run after the top-level middleware file. You can refer to the NextJS Middleware Execution Order Documentation for more information.
Now let us write a simple middleware function -
const middleware = (req, ev) => {
return new Response(req.ua.os.name);
};
export default middleware;
This will show the operating system of the user on the page. We were streaming HTML to the user but now let us see how we can rewrite routes under the hood.
A middleware that rewrites the route under the hood
First, we will create a new file called [os].js
and copy in the index.js
code into there, just replacing the function name.
[os].js
-
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
export default function Home() {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1 className={styles.title}>
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>
</main>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by Vercel
and Replit
</a>
</footer>
</div>
)
}
Now let us go back to our _middleware.js
file and make some changes. First we will import NextResponse
import {NextResponse} from "next/server"
Now, let us store the user's operating system in a variable
const os = req.ua.os.name
Now, we can rewrite the URL to the new route we created and that is where NextResponse
comes in.
return NextResponse.rewrite(`/${os}`) // This return is necessary
This is how out _middleware.js
files should look now -
import {NextResponse} from "next/server"
const middleware = (req, ev) => {
const os = req.ua.os.name
return NextResponse.rewrite(`/${os}`)
};
export default middleware;
Now let us import Next Router in our new route
import {useRouter} from "next/router"
As this is a react hook, we need to create a new instance of the NextJS router inside of the function being returned.
const router = useRouter()
We can get the operating system of the user from the URL parameter we rewrote earlier in the middleware
const os = router.query.os
At last, let us show the user what operating system they are using
<h1 className={styles.title}>
You are using the {os} operating system
</h1>
At last this is how are [os].js
should look like -
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import {useRouter} from "next/router"
export default function Os() {
const router = useRouter()
const os = router.query.os
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1 className={styles.title}>
You are using the {os} operating system
</h1>
</main>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by Vercel
and Replit
</a>
</footer>
</div>
)
}
Now if we visit the home page, we should see this (note that I had removed some of the nextjs boilerplate code) -
I can emulate the browser user agent using the browser dev tools and we get a different result for the operating system -
Now, note that the URL is https://nextjs-12-middleware.anishde12020.repl.co/
and not https://nextjs-12-middleware.anishde12020.repl.co/Linux
or https://nextjs-12-middleware.anishde12020.repl.co/Mac Os
. This is because, we are re-writing the request in the middleware and hence there is no change on the client side.
How does NextJS middleware work under the hood?
NextJS middleware make use of Vercel's Edge Functions which run on the V8 Engine. The V8 Engine is a javascript engine written in C++ and is maintained by Google. It is considerably faster than running NodeJS on a virtual machine or a container and Vercel claims these edge functions to be instantaneous.
Limitations of edge functions
Some NodeJS APIs are not available with middleware (as they are edge functions) such as the filesystem API. Also, some NodeJS modules won't work as only modules which have implemented ES Modules and do not use any native NodeJS APIs are allowed. For more information, see here
Wrapping Up
So far, we have seen the power of middleware and how this is going to be a huge feature for developers. Things like serving localized sites, providing region-based discounts, authentication, bot detection and much more can be done quickly and easily by using middleware.
The team at Vercel has also put together many examples using middleware. Check them out here
That is it for this post, feel free to leave down a comment below and you can also reach out to me on Twitter