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 14 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
252 changes: 193 additions & 59 deletions docs/advanced-features/middleware.md
Original file line number Diff line number Diff line change
@@ -1,105 +1,239 @@
---
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. |

| `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 enables you to use code over configuration. This gives you full flexibility in Next.js, because you can run code _before_ a request is completed. Middleware runs _before_ the CDN cache lookup, then based on the incoming request, you can modify the response by rewriting, redirecting, adding headers, or setting cookies.
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


- A single `middleware.ts` file is created at your projects root, with an exported function (the file extension can be either `.ts` or `.js`)
molebox marked this conversation as resolved.
Show resolved Hide resolved
- 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.

- 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, it is not permitted to change the response body: you can only set response headers.
Returning a body from Middleware will result in a `500` server error and an explicit response message.
molebox marked this conversation as resolved.
Show resolved Hide resolved

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`. This 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).
molebox marked this conversation as resolved.
Show resolved Hide resolved

## 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


## Usage
To begin using Middleware, follow the steps below:

1. Install the canary version of Next.js:
1. Install the latest version of Next.js:

```jsx
npm install next@canary
```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*'
}
```

In this example, we use the standard Web API Response ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Response)).
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.
molebox marked this conversation as resolved.
Show resolved Hide resolved

## API
### 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


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.
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.

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.
#### Match a single path

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

The function signature:
#### Match multiple paths

```ts
import type { NextFetchEvent } from 'next/server'
```js
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
```

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'

export type Middleware = (
request: NextRequest,
event: NextFetchEvent
) => Promise<Response | undefined> | Response | undefined
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/about')) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}

if (request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.rewrite(new URL('/dashboard/user', request.url))
}
}
```

### Using cookies in Middleware

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).

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

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