Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new middleware docs #37514

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 201 additions & 59 deletions docs/advanced-features/middleware.md
Original file line number Diff line number Diff line change
@@ -1,105 +1,247 @@
---
description: Learn how to use Middleware in Next.js to run code before a request is completed.
description: Learn how to use Middleware to run code before a request is completed.
---

# Middleware (Beta)
# Middleware

<details open>
<summary><b>Version History</b></summary>
<summary><b>Version History</b></summary>

| Version | Changes |
| --------- | ------------------------------------------------------------------------------------------------------- |
| `canary` | Preparing for stability, see [upgrade guide](https://nextjs.org/docs/messages/middleware-upgrade-guide) |
| `v12.0.9` | Enforce absolute URLs in Edge Runtime ([PR](https://github.com/vercel/next.js/pull/33410)) |
| `v12.0.0` | Middleware (Beta) added. |
| Version | Changes |

| --------- | ------------------------------------------------------------------------------------------ |

| `v12.2.0` | Middleware GA |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| `v12.2.0` | Middleware GA |
| `v12.2.0` | Middleware is stable |


| `v12.0.9` | Enforce absolute URLs in Edge Runtime ([PR](https://github.com/vercel/next.js/pull/33410)) |

| `v12.0.0` | Middleware (Beta) added |

</details>

Middleware enables you to run code before a request is completed. Based on the user's incoming request, you can modify the response by rewriting, redirecting, adding headers, or even streaming HTML.
Middleware allows you to run code before a request is completed, then based on the incoming request, you can modify the response by rewriting, redirecting, adding headers, or setting cookies.

When a request is made, it will first hit the Middleware, _then_ the cache, meaning you can personalize static content and implement authentication, run A/B tests, deliver personalizes pages based on geolocation, and perform bot protection.
molebox marked this conversation as resolved.
Show resolved Hide resolved

## Summary of Middleware
molebox marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## Summary of Middleware
## Quickstart

Or similar


- You create a single `middleware.ts` or `middleware.js` file at your projects root, with an exported function
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- You create a single `middleware.ts` or `middleware.js` file at your projects root, with an exported function
- Create a `middleware.ts` (or `middleware.js`) file at the root of your project

- The function can be a named, or default export. If the function is a named export, then is **must** be called `middleware`. For a default export, you are free to name it anything you like
molebox marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- The function can be a named, or default export. If the function is a named export, then is **must** be called `middleware`. For a default export, you are free to name it anything you like
- Export a function named `middleware`

This feels like a strange this to emphasize so early. Let's put them on the happy path and then add a caveat somewhere at the bottom of the page for default exports.

```js
//named export
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//named export
// named export

export function middleware() {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we can move this down to that section

// default export
export default function custom() {}
```
- The function can be `async` if you are running asynchronous code
- Middleware executes on _all_ requests, including `/_next`
- Node.js APIs are [**not supported in this environment**](https://edge-runtime.vercel.sh/#developing-edge-functions-locally)

### Permitted response types

When using Middleware, you cannot change the response body: you can only set response headers.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's lead with the section below saying "API allows you to..."

And then we need to add an example for how you handle response bodies through an API route instead.

Returning a body from Middleware will result in a `500` server error and an explicit response message.

The [`NextResponse`](#nextresponse) API allows you to:

- `redirect` the incoming request to a different URL
- `rewrite` the response by displaying a given URL
- Set response cookies
- Set response headers

With Middleware you can implement A/B testing, authentication, feature flags, bot protection, and more. See our [examples repository](https://github.com/vercel/examples/tree/main/edge-functions) for code examples.

### Deploying Middleware

Middleware uses a the [Edge Runtime](https://edge-runtime.vercel.sh/features) and supports standard Web APIs like `fetch`. Middleware works out of the box using `next start`, as well as on Edge platforms like Vercel, which use [Edge Functions](https://vercel.com/docs/concepts/functions/vercel-edge-functions).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Middleware uses a the [Edge Runtime](https://edge-runtime.vercel.sh/features) and supports standard Web APIs like `fetch`. Middleware works out of the box using `next start`, as well as on Edge platforms like Vercel, which use [Edge Functions](https://vercel.com/docs/concepts/functions/vercel-edge-functions).
Middleware uses the [Edge Runtime](https://edge-runtime.vercel.sh) which supports standard Web APIs like `fetch` and more. Middleware works out of the box using `next start`, as well as on Edge platforms like Vercel, which use [Edge Functions](https://vercel.com/docs/concepts/functions/vercel-edge-functions).


## Usage
## Using Middleware
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this should be combined with the quickstart section above


1. Install the canary version of Next.js:
To begin using Middleware, follow the steps below:

```jsx
npm install next@canary
1. Install the latest version of Next.js:

```bash
npm install next@latest
```

2. Then, create a `middleware.ts` file under your project root directory.
2. Create a `middleware.ts` file under your project root directory. Note that the file extension can be either `.ts` _or_ `.js`

3. Finally, export a middleware function from the `middleware.ts` file.
3. Export a middleware function from the `middleware.ts` file. The following example assumes you have a route called `/about` in your project, and that you want to rewrite to a new route `/about-2` whenever someone visits `/about`:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can let the code explain this with a comment, e.g. "change this to whatever route you want"


```jsx
```typescript
// middleware.ts

import type { NextRequest, NextResponse } from 'next/server'
import { areCredentialsValid } from '../lib'
import { NextResponse } from 'next/server
import type { NextRequest } from 'next/server'

export function middleware(req: NextRequest) {
if (areCredentialsValid(req.headers.get('authorization'))) {
return NextResponse.next()
}
return NextResponse.redirect(
new URL(`/login?from=${req.nextUrl.pathname}`, req.url)
)
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/about-2', request.url));
}

// config with custom matcher
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here

export const config = {
matcher: '/about/:path*'
}
```

Middleware will be invoked for **every route in your project**. There are two ways to define which paths the middleware should be run on: with a custom matcher config or with conditional statements.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been mentioned 3 times now when talking about the order of execution and which files it runs on. Let's pull this into a section.

  • here's the order the runs (before the cache, then x)
  • here's how you control which pages it runs on


### Match paths based on custom matcher config
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which is probably this section reworked a bit


To decide which route the Middleware should be run on, you can use a custom matcher config to filter on specific paths. The matcher property can be used to define either a single path, or using an array syntax, multiple paths.

#### Match a single path

```js
export const config = {
matcher: '/about/:path*',
molebox marked this conversation as resolved.
Show resolved Hide resolved
}
```

#### Match multiple paths

```js
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
```

In this example, we use the standard Web API Response ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Response)).
Note that while the config option is the preferred method, **as it does not get invoked on every request**, you can also use conditional statements to only run the Middleware when it matches specific paths.

### Match paths based on conditional statements

```typescript
// middleware.ts

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

## API
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/about')) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}

Middleware is created by using a `middleware` function that lives inside a `middleware` file. Its API is based upon the native [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent), [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response), and [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) objects.
if (request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.rewrite(new URL('/dashboard/user', request.url))
}
}
```

These native Web API objects are extended to give you more control over how you manipulate and configure a response, based on the incoming requests.
### Using cookies in Middleware

The function signature:
The cookies API extends [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), and follows a get/set model, allowing you to get, set, and delete cookies within your middleware function. It includes methods like [entries](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries) and [values](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries).

```ts
import type { NextFetchEvent } from 'next/server'
```typescript
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export type Middleware = (
request: NextRequest,
event: NextFetchEvent
) => Promise<Response | undefined> | Response | undefined
export function middleware(request: NextRequest) {
// Accessing cookies on the response object
const response = NextResponse.next()
// set a cookie
response.cookies.set('vercel', 'fast')
// set another cookie with options
response.cookies.set('nextjs', 'awesome', { path: '/test' })
// get all the details of a cookie
const { value, options } = response.cookies.getWithOptions('vercel')
console.log(value) // => 'fast'
console.log(options) // => { Path: '/test' }
// deleting a cookie will mark it as expired
response.cookies.delete('vercel')
// clear all cookies means mark all of them as expired
response.cookies.clear()

// Accessing cookies on the request object
// get a cookie
const cookie = request.cookies.get('vercel')
console.log(cookie) // => 'fast'
// get all cookies
const allCookies = request.cookies.entries()
console.log(allCookies) // => [{ key: 'vercel', value: 'fast' }]
// delete a cookie
request.cookies.delete('vercel')
// clear all cookies
request.cookies.clear()

return response
}
```

The function can be a default export and as such, does **not** have to be named `middleware`. Though this is a convention. Also note that you only need to make the function `async` if you are running asynchronous code.
### How to check if Middleware is invoked for pages

Read the full [Middleware API reference](/docs/api-reference/edge-runtime.md), note [Node.js APIs are not supported in this environment](/docs/api-reference/edge-runtime.md#unsupported-apis)
To check if Middleware is being invoked for certain pages or assets, you can use the web standard, [`URLPattern`](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) API. The following example shows how you can accomplish routing pattern matching using the URLPattern API.

## Examples
```typescript
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

Middleware can be used for anything that shares logic for a set of pages, including:
// Assignment of a URL pattern
const PATTERNS = [
[
// Define a pattern based on the presence of pathname
new URLPattern({ pathname: '/:locale/:slug' }),
// The handler associated with the pattern returns the detected groups
({ pathname }) => pathname.groups,
],
]

// The params function tries to match the incoming URL against the list of patterns. It exists early if it finds the first matching result
const params = (url) => {
// Remove the query parameters from the incoming URL
const input = url.split('?')[0]
let result = {}

// Iterating over the previously declared list of patterns
for (const [pattern, handler] of PATTERNS) {
// `patternResult` will contain info about the successful match
const patternResult = pattern.exec(input)

// If the pathname matches, then resolve using the handler associated with the pattern
if (patternResult !== null && 'pathname' in patternResult) {
result = handler(patternResult)
break
}
}
return result
}

- [Authentication](https://github.com/vercel/examples/tree/main/edge-functions)
- [Bot protection](https://github.com/vercel/examples/tree/main/edge-functions)
- [Redirects and rewrites](https://github.com/vercel/examples/tree/main/edge-functions)
- [Handling unsupported browsers](https://github.com/vercel/examples/tree/main/edge-functions/user-agent-based-rendering)
- [Feature flags and A/B tests](https://github.com/vercel/examples/tree/main/edge-functions)
- [Advanced i18n routing requirements](https://github.com/vercel/examples/tree/main/edge-functions)
// Middleware for rewriting URLs into locale subdomain URLs
// Turns `https://{domain}/{locale}/{slug}` into `https://{locale}.{domain}/{slug}`
export function middleware(request: NextRequest) {
const { locale, slug } = params(request.url)
if (locale && slug) {
const { search, protocol, host } = request.nextUrl
const url = new URL(`${protocol}//${locale}.${host}/${slug}${search}`)
return NextResponse.redirect(url)
}
}
```

## Execution Order
## Middleware API

Middleware runs directly after `redirects` and `headers`, before the first filesystem lookup. This excludes `/_next` files.
Next.js Middleware uses the [Edge Runtime API](https://edge-runtime.vercel.sh/features/available-apis), which includes the following APIs:

## Deployment
- [Network APIs](https://edge-runtime.vercel.sh/features/available-apis#network-apis)
- [Encoding APIs](https://edge-runtime.vercel.sh/features/available-apis#encoding-apis)
- [Web Stream APIs](https://edge-runtime.vercel.sh/features/available-apis#web-stream-apis)
- [Web Crypto APIs](https://edge-runtime.vercel.sh/features/available-apis#web-crypto-apis)
- [Web Standard APIs](https://edge-runtime.vercel.sh/features/available-apis#web-standards-apis)
- [V8 Primitives](https://edge-runtime.vercel.sh/features/available-apis#v8-primitives)

Middleware uses a [strict runtime](/docs/api-reference/edge-runtime.md) that supports standard Web APIs like `fetch`. This works out of the box using `next start`, as well as on Edge platforms like Vercel, which use [Edge Functions](http://www.vercel.com/edge).
See the [Edge Runtime documentation](https://edge-runtime.vercel.sh/) for a full list of the available APIs.

## Related
In addition to these APIs, Next.js Middleware comes with built in helpers that are based upon the native [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent), [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response), and [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) objects.

<div class="card">
<a href="/docs/api-reference/next/server.md">
<b>Middleware API Reference</b>
<small>Learn more about the supported APIs for Middleware.</small>
</a>
</div>
See the [`next/server`](/docs/api-reference/next/server.md) documentation for more information.

## Related

<div class="card">
<a href="/docs/api-reference/edge-runtime.md">
<a href="https://edge-runtime.vercel.sh/">
<b>Edge Runtime</b>
<small>Learn more about the supported Web APIs available.</small>
<small>Learn more about the supported APIs for Next.js Middleware.</small>
</a>
</div>
Loading