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

Update Middleware documentation #37992

Closed
wants to merge 48 commits into from
Closed
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
f903c34
Initial outline added for middleware docs
molebox Jun 7, 2022
08c812e
Feedback changes - not included new user agent yet
molebox Jun 10, 2022
8f10228
Merge branch 'canary' into middleware-docs
molebox Jun 20, 2022
9476a0b
edge api routes docs
molebox Jun 22, 2022
879d58c
middleware page some cleanup
molebox Jun 22, 2022
273dec4
Merge branch 'canary' into middleware-docs
molebox Jun 22, 2022
861b526
moved api stuff to next server, added useragent
molebox Jun 22, 2022
bf087f4
waitUntil example added
molebox Jun 22, 2022
47e37b5
Updated env vars section
molebox Jun 22, 2022
cae2e64
Merge branch 'canary' into middleware-docs
molebox Jun 22, 2022
4e9c1bf
Added cookies on the request object example
molebox Jun 22, 2022
3176b4e
Merge branch 'canary' into middleware-docs
molebox Jun 22, 2022
7291023
fixed naming
molebox Jun 22, 2022
82b18b8
fixed import typo
molebox Jun 22, 2022
b5d0d41
Merge branch 'canary' into middleware-docs
molebox Jun 22, 2022
45e528f
Amy feedback
molebox Jun 22, 2022
1e97c04
Update docs/advanced-features/middleware.md
molebox Jun 23, 2022
d97c754
first commit
ismaelrumzan Jun 24, 2022
a5f5a8e
more fixes
ismaelrumzan Jun 24, 2022
b172775
re-structuring based on feedback
ismaelrumzan Jun 24, 2022
098db80
changes to middleware.md
ismaelrumzan Jun 24, 2022
175a14f
updates to edge-api-routes
ismaelrumzan Jun 24, 2022
b91774b
code typo
ismaelrumzan Jun 24, 2022
c090d71
Update docs/advanced-features/middleware.md
leerob Jun 25, 2022
096f75a
Update edge-api-routes.md
leerob Jun 26, 2022
7246b79
Update Edge Runtime API docs
leerob Jun 26, 2022
d5cab08
Merge branch 'canary' into middleware-docs-update
leerob Jun 26, 2022
f916aea
Clarify API docs
leerob Jun 26, 2022
96dbd4c
Update
leerob Jun 26, 2022
1b248cf
Update docs/advanced-features/middleware.md
leerob Jun 26, 2022
bffe484
Update switchable runtime docs
leerob Jun 26, 2022
4f00571
Update response
leerob Jun 26, 2022
c30c0c7
experimental-edge
leerob Jun 26, 2022
af91624
Update docs/advanced-features/middleware.md
leerob Jun 26, 2022
405ed88
Add example of forwarding headers and address comments
leerob Jun 27, 2022
6639915
Add proxying headers to middleware upgrade guide
leerob Jun 27, 2022
c6e0190
Merge branch 'canary' into middleware-docs-update
balazsorban44 Jun 27, 2022
400ca74
Apply suggestions from code review
leerob Jun 27, 2022
4080629
fix middleware directory spec
ismaelrumzan Jun 27, 2022
2f42c0a
localization explanation
ismaelrumzan Jun 27, 2022
6ef2b6f
update detecting runtime
ismaelrumzan Jun 28, 2022
fce31ff
Update docs/advanced-features/react-18/switchable-runtime.md
ismaelrumzan Jun 28, 2022
d0f3623
fix prettier issues
ismaelrumzan Jun 28, 2022
80f5fb9
Merge branch 'middleware-docs-update' of https://github.com/ismaelrum…
ismaelrumzan Jun 28, 2022
003fe75
table fix
ismaelrumzan Jun 28, 2022
bb2a562
Update docs/advanced-features/react-18/switchable-runtime.md
ismaelrumzan Jun 28, 2022
efe31e6
Update docs/api-routes/edge-api-routes.md
ismaelrumzan Jun 28, 2022
877cd0c
Update docs/advanced-features/react-18/switchable-runtime.md
leerob Jun 28, 2022
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
271 changes: 212 additions & 59 deletions docs/advanced-features/middleware.md
Original file line number Diff line number Diff line change
@@ -1,105 +1,258 @@
---
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 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.

Middleware runs _before_ cached content, so you can personalize static files and pages. Common examples of Middleware would be authentication, A/B testing, localized pages, bot protection, and more.
leerob marked this conversation as resolved.
Show resolved Hide resolved
leerob marked this conversation as resolved.
Show resolved Hide resolved
ismaelrumzan marked this conversation as resolved.
Show resolved Hide resolved

## Quickstart

- Create a `middleware.ts` (or `middleware.js`) file at the root of your project
- Export a function named `middleware`
```js
// named export
export function middleware() {}
// 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

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

When using Middleware, you cannot 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.

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

### Using Middleware

## 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
leerob marked this conversation as resolved.
Show resolved Hide resolved
```

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:

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

import type { NextRequest, NextResponse } from 'next/server'
import { areCredentialsValid } from '../lib'
import { NextResponse } from 'next/server
leerob marked this conversation as resolved.
Show resolved Hide resolved
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) {
// change this to the route in your project that you would like the rewrite to go to
return NextResponse.redirect(new URL('/about-2', request.url));
}

// config with custom matcher
export const config = {
// change this to the route in your project that you would like the rewrite to be applied to
matcher: '/about/:path*'
}
```

## Matching paths

Middleware will be invoked for **every route in your project**. There are two ways to define which paths the middleware should be run on:

1. With a custom matcher config
2. With conditional statements

### Match paths based on custom matcher config

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*',
}
```

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

## API
### Match paths based on conditional statements

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.
```typescript
// middleware.ts

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.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

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

```ts
import type { NextFetchEvent } from 'next/server'
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 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
## Using default exports in Middleware

Middleware runs directly after `redirects` and `headers`, before the first filesystem lookup. This excludes `/_next` files.
The Middleware 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.

## Deployment
## Middleware API

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).
leerob marked this conversation as resolved.
Show resolved Hide resolved
Next.js Middleware uses the [Edge Runtime API](https://edge-runtime.vercel.sh/features/available-apis), which includes the following APIs:

## Related
- [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)

<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 [Edge Runtime documentation](https://edge-runtime.vercel.sh/) for a full list of the available APIs.

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.

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