Skip to content

Commit

Permalink
Middleware docs update (#38111)
Browse files Browse the repository at this point in the history
* Initial outline added for middleware docs

* Feedback changes - not included new user agent yet

* edge api routes docs

* middleware page some cleanup

* moved api stuff to next server, added useragent

* waitUntil example added

* Updated env vars section

* Added cookies on the request object example

* fixed naming

* fixed import typo

* Amy feedback

* Update docs/advanced-features/middleware.md

Co-authored-by: Amy Burns <amy.burns@vercel.com>

* first commit

* more fixes

* re-structuring based on feedback

* changes to middleware.md

* updates to edge-api-routes

* code typo

* Update docs/advanced-features/middleware.md

Co-authored-by: JJ Kasper <jj@jjsweb.site>

* Update edge-api-routes.md

* Update Edge Runtime API docs

* Clarify API docs

* Update

* Update docs/advanced-features/middleware.md

* Update switchable runtime docs

* Update response

* experimental-edge

* Update docs/advanced-features/middleware.md

* Add example of forwarding headers and address comments

* Add proxying headers to middleware upgrade guide

* Apply suggestions from code review

Co-authored-by: Jeff Escalante <jescalan@users.noreply.github.com>

* fix middleware directory spec

* localization explanation

* update detecting runtime

* Update docs/advanced-features/react-18/switchable-runtime.md

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* fix prettier issues

* table fix

* Update docs/advanced-features/react-18/switchable-runtime.md

Co-authored-by: Jeff Escalante <jescalan@users.noreply.github.com>

* Update docs/api-routes/edge-api-routes.md

Co-authored-by: Jeff Escalante <jescalan@users.noreply.github.com>

* lint-fix

Co-authored-by: molebox <hello@richardhaines.dev>
Co-authored-by: Amy Burns <amy.burns@vercel.com>
Co-authored-by: Ismael Rumzan <ismael.rumzan@gmail.com>
Co-authored-by: Lee Robinson <me@leerob.io>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Jeff Escalante <jescalan@users.noreply.github.com>
Co-authored-by: Ismael <ismael@vercel.com>
  • Loading branch information
8 people committed Jun 28, 2022
1 parent 632feb6 commit 8e161e1
Show file tree
Hide file tree
Showing 7 changed files with 516 additions and 262 deletions.
177 changes: 122 additions & 55 deletions docs/advanced-features/middleware.md
Original file line number Diff line number Diff line change
@@ -1,95 +1,162 @@
---
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.

## Usage
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. Regarding localized pages, you can start with [i18n routing](/docs/advanced-features/i18n-routing) and implement Middleware for more advanced use cases.

1. Install the canary version of Next.js:
> **Note:** If you were using Middleware prior to `12.2`, please see the [upgrade guide](https://nextjs.org/docs/messages/middleware-upgrade-guide).
```jsx
npm install next@canary
```
## Using Middleware

To begin using Middleware, follow the steps below:

2. Then, create a `middleware.ts` file under your project root directory.
1. Install the latest version of Next.js:

3. Finally, export a middleware function from the `middleware.ts` file.
```bash
npm install next@latest
```

```jsx
2. Create a `middleware.ts` (or `.js`) file at the same level as your `pages` directory
3. Export a middleware function from the `middleware.ts` file:

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

import type { NextRequest, NextResponse } from 'next/server'
import { areCredentialsValid } from '../lib'
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/about-2', request.url))
}

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)
)
// See "Matching Paths" below to learn more
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)).
## Matching Paths

## API
Middleware will be invoked for **every route in your project**. The following is the execution order:

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.
1. `headers` from `next.config.js`
2. `redirects` from `next.config.js`
3. Middleware (`rewrites`, `redirects`, etc.)
4. `beforeFiles` (`rewrites`) from `next.config.js`
5. Filesystem routes (`public/`, `_next/static/`, Pages, etc.)
6. `afterFiles` (`rewrites`) from `next.config.js`
7. Dynamic Routes (`/blog/[slug]`)
8. `fallback` (`rewrites`) from `next.config.js`

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.
There are two ways to define which paths Middleware will run on:

The function signature:
1. Custom matcher config
2. Conditional statements

```ts
import type { NextFetchEvent } from 'next/server'
import type { NextRequest } from 'next/server'
### Matcher

export type Middleware = (
request: NextRequest,
event: NextFetchEvent
) => Promise<Response | undefined> | Response | undefined
`matcher` allows you to filter Middleware to run on specific paths.

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

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.
You can match a single path or multiple paths with an array syntax:

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

### Conditional Statements

```typescript
// middleware.ts

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

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))
}
}
```

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

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

Middleware can be used for anything that shares logic for a set of pages, including:
- `redirect` the incoming request to a different URL
- `rewrite` the response by displaying a given URL
- Set response cookies
- Set response headers

- [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)
To produce a response from Middleware, you should `rewrite` to a route ([Page](/docs/basic-features/pages.md) or [Edge API Route](/docs/api-routes/edge-api-routes.md)) that produces a response.

## Execution Order
## Using Cookies

Middleware runs directly after `redirects` and `headers`, before the first filesystem lookup. This excludes `/_next` files.
The `cookies` API extends [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) and allows you to `get`, `set`, and `delete` cookies. It also 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).

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

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).
export function middleware(request: NextRequest) {
// Setting cookies on the response
const response = NextResponse.next()
response.cookies.set('vercel', 'fast')
response.cookies.set('vercel', 'fast', { path: '/test' })

// Getting cookies from the request
const cookie = request.cookies.get('vercel')
console.log(cookie) // => 'fast'
const allCookies = request.cookies.entries()
console.log(allCookies) // => [{ key: 'vercel', value: 'fast' }]
const { value, options } = response.cookies.getWithOptions('vercel')
console.log(value) // => 'fast'
console.log(options) // => { Path: '/test' }

// Deleting cookies
response.cookies.delete('vercel')
response.cookies.clear()

return response
}
```

## Related

<div class="card">
<a href="/docs/api-reference/edge-runtime.md">
<b>Edge Runtime</b>
<small>Learn more about the supported Web APIs available.</small>
</a>
</div>

<div class="card">
<a href="/docs/api-reference/next/server.md">
<b>Middleware API Reference</b>
Expand All @@ -98,8 +165,8 @@ Middleware uses a [strict runtime](/docs/api-reference/edge-runtime.md) that sup
</div>

<div class="card">
<a href="/docs/api-reference/edge-runtime.md">
<b>Edge Runtime</b>
<small>Learn more about the supported Web APIs available.</small>
<a href="/docs/api-routes/edge-api-routes.md">
<b>Edge API Routes</b>
<small>Build high performance APIs in Next.js. </small>
</a>
</div>
63 changes: 56 additions & 7 deletions docs/advanced-features/react-18/switchable-runtime.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
# Switchable Runtime (Alpha)

By default, Next.js uses Node.js as the runtime for page rendering, including pre-rendering and server-side rendering.
Next.js has two _server runtimes_ to run your application: the **Node.js Runtime** (default) and the [**Edge Runtime**](/docs/api-reference/edge-runtime.md). When server-rendering or serving API routes, the application code will be executed in the Node.js Runtime by default; and for [Middleware](/docs/middleware.md), it will be running in the Edge Runtime.

> Note: `runtime` option only effects pages but not middleware
|   | Node (server) | Node (lambda) | Edge |
| ------------------------------------------------------------------------------------------- | ------------- | ------------- | ---------------- |
| [Cold Boot](https://vercel.com/docs/concepts/functions/conceptual-model#cold-and-hot-boots) | / | ~250ms | Instant |
| [HTTP Streaming](https://github.com/reactwg/react-18/discussions/37) | Yes | No | Yes |
| IO | All | All | `fetch` |
| Scalability | / | High | Highest |
| Security | Normal | High | High |
| Latency | Normal | Low | Lowest |
| Code Size | / | 50MB | 1MB |
| NPM Packages | All | All | A smaller subset |

If you have [React 18](/docs/advanced-features/react-18/overview) installed, there is a new experimental feature that lets you switch the page runtime between Node.js and the [Edge Runtime](/docs/api-reference/edge-runtime). Changing the runtime affects [SSR streaming](/docs/advanced-features/react-18/streaming) and [Server Components](/docs/advanced-features/react-18/server-components) features, as well.
Next.js' default runtime configuration is good for most use cases, but there’re still many reasons to change to one runtime over the other one. For example, to enable [React 18's](/docs/advanced-features/react-18/overview) [SSR streaming](/docs/advanced-features/react-18/streaming.md) feature, you need to use a runtime that is compatible with Web Streams. For API routes that rely on native Node.js APIs, they need to run with the **Node.js Runtime**. However, if an API only uses something like cookie-based authentication, using Middleware and the [**Edge Runtime**](/docs/api-reference/edge-runtime.md) will be a better choice due to its lower latency as well as better scalability.

Starting with `12.2`, Next.js enables you to customize the runtime for each Next.js route, for both Pages and API routes.

## Global Runtime Option

Expand All @@ -14,25 +25,63 @@ You can set the experimental option `runtime` to either `'nodejs'` or `'experime
// next.config.js
module.exports = {
experimental: {
runtime: 'nodejs',
runtime: 'experimental-edge',
},
}
```

This option determines which runtime should be used as the default rendering runtime for all pages.

You can detect which runtime you're using by looking at the `process.env.NEXT_RUNTIME` Environment Variable during runtime, and examining the `options.nextRuntime` variable during webpack compilation.

## Page Runtime Option

On each page, you can optionally export a `runtime` config set to either `'nodejs'` or `'experimental-edge'`:

```jsx
// pages/index.js
export default function Index () { ... }

export function getServerSideProps() { ... }

export const config = {
runtime: 'nodejs',
runtime: 'experimental-edge',
}
```

When both the per-page runtime and global runtime are set, the per-page runtime overrides the global runtime. If the per-page runtime is _not_ set, the global runtime option will be used.

You can refer to the [Switchable Next.js Runtime RFC](https://github.com/vercel/next.js/discussions/34179) for more information.
## Edge API Routes

[Edge API Routes](/docs/api-routes/edge-api-routes.md) enable you to build high performance APIs with Next.js using the Edge Runtime.

```typescript
export const config = {
runtime: 'experimental-edge',
}

export default (req) => new Response('Hello world!')
```

## Related

<div class="card">
<a href="/docs/api-reference/edge-runtime.md">
<b>Edge Runtime</b>
<small>Learn more about the supported Web APIs available.</small>
</a>
</div>

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

**Note:** The page runtime option is not supported in [API Routes](/docs/api-routes/introduction.md) currently.
<div class="card">
<a href="/docs/api-routes/edge-api-routes.md">
<b>Edge API Routes</b>
<small>Build high performance APIs in Next.js. </small>
</a>
</div>
Loading

0 comments on commit 8e161e1

Please sign in to comment.