diff --git a/docs/advanced-features/middleware.md b/docs/advanced-features/middleware.md
index 820e6268b48a2..af5a8ecb2d459 100644
--- a/docs/advanced-features/middleware.md
+++ b/docs/advanced-features/middleware.md
@@ -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
- Version History
+Version History
-| 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 |
-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 personalized pages based on geolocation, and perform bot protection.
+
+## Summary of Middleware
+
+- You create a single `middleware.ts` or `middleware.js` file at your projects root, with an exported function
+- 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
+ ```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
+
+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.
+
+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).
-## Usage
+## Using Middleware
-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`:
-```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
+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.
+
+### 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.
+
+### 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
+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.
-
+See the [`next/server`](/docs/api-reference/next/server.md) documentation for more information.
+
+## Related
diff --git a/docs/api-reference/next/server.md b/docs/api-reference/next/server.md
index 2316da4a9c379..f2f0b1ef25c09 100644
--- a/docs/api-reference/next/server.md
+++ b/docs/api-reference/next/server.md
@@ -1,48 +1,38 @@
---
-description: Use Middleware to run code before a request is completed.
+description: Learn about the Edge Middleware helpers and APIs.
---
# next/server
The `next/server` module provides several exports for server-only helpers, such as [Middleware](/docs/middleware.md).
-## NextMiddleware
-
-Middleware is created by using a `middleware` function that lives inside a `middleware` file. The Middleware API is based upon the native [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request), [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent), and [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects.
-
-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.
-
-The function signature is defined as follows:
-
-```typescript
-type NextMiddlewareResult = NextResponse | Response | null | undefined
-
-type NextMiddleware = (
- request: NextRequest,
- event: NextFetchEvent
-) => NextMiddlewareResult | Promise
-```
-
-It can be imported from `next/server` with the following:
-
-```typescript
-import type { NextMiddleware } from 'next/server'
-```
-
-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.
-
## NextRequest
The `NextRequest` object is an extension of the native [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) interface, with the following added methods and properties:
-- `cookies` - A [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) with cookies from the `Request`
-- `nextUrl` - Includes an extended, parsed, URL object that gives you access to Next.js specific properties such as `pathname`, `basePath`, `trailingSlash` and `i18n`
-- `ip` - Has the IP address of the `Request`
-- `geo` - (Optional) Has the geo location from the `Request`, provided by your hosting platform
+- `cookies` - A [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) with cookies from the `Request`. See [Using cookies in Edge Middleware](#using-cookies-in-edge-middleware)
+- `nextUrl`: Includes an extended, parsed, URL object that gives you access to Next.js specific properties such as `pathname`, `basePath`, `trailingSlash` and `i18n`. Includes the following properties:
+ - `basePath` (`string`)
+ - `buildId` (`string || undefined`)
+ - `defaultLocale` (`string || undefined`)
+ - `domainLocale`
+ - `defaultLocale`: (`string`)
+ - `domain`: (`string`)
+ - `http`: (`boolean || undefined`)
+ - `locales`: (`string[] || undefined`)
+ - `locale` (`string || undefined`)
+ - `url` (`URL`)
+- `ip`: (`string || undefined`) - Has the IP address of the `Request`
+- `geo` - Has the geographic location from the `Request`. This information is provided by your hosting platform. Includes the following properties:
+ - `city` (`string || undefined`)
+ - `country` (`string || undefined`)
+ - `region` (`string || undefined`)
+ - `latitude` (`string || undefined`)
+ - `longitude` (`string || undefined`)
You can use the `NextRequest` object as a direct replacement for the native `Request` interface, giving you more control over how you manipulate the request.
-`NextRequest` is fully typed and can be imported from `next/server`.
+`NextRequest` can be imported from `next/server`:
```typescript
import type { NextRequest } from 'next/server'
@@ -52,9 +42,25 @@ import type { NextRequest } from 'next/server'
The `NextFetchEvent` object extends the native [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent) object, and includes the [`waitUntil()`](https://developer.mozilla.org/en-US/docs/Web/API/ExtendableEvent/waitUntil) method.
-The `waitUntil()` method can be used to prolong the execution of the function, after the response has been sent. In practice this means that you can send a response, then continue the function execution if you have other background work to make.
+The `waitUntil()` method can be used to prolong the execution of the function if you have other background work to make.
+
+```typescript
+import { NextResponse } from 'next/server'
+import type { NextFetchEvent, NextRequest } from 'next/server'
-The `event` object is fully typed and can be imported from `next/server`.
+export async function middleware(req: NextRequest, event: NextFetchEvent) {
+ event.waitUntil(
+ fetch('https://my-analytics-platform.com', {
+ method: 'POST',
+ body: JSON.stringify({ pathname: req.nextUrl.pathname }),
+ })
+ )
+
+ return NextResponse.next()
+}
+```
+
+The `NextFetchEvent` object can be imported from `next/server`:
```typescript
import type { NextFetchEvent } from 'next/server'
@@ -68,25 +74,7 @@ The `NextResponse` class extends the native [`Response`](https://developer.mozil
Public methods are available on an instance of the `NextResponse` class. Depending on your use case, you can create an instance and assign to a variable, then access the following public methods:
-- `cookies` - The `Cookies` API extends [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), including 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
-export function middleware() {
- const response = new NextResponse()
- // 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()
-}
-```
+- `cookies` - A [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) with the cookies in the `Response`
### Static methods
@@ -100,66 +88,48 @@ The following static methods are available on the `NextResponse` class directly:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
-export function middleware(req: NextRequest) {
+export function middleware(request: NextRequest) {
// if the request is coming from New York, redirect to the home page
- if (req.geo.city === 'New York') {
+ if (request.geo.city === 'New York') {
return NextResponse.redirect('/home')
// if the request is coming from London, rewrite to a special page
- } else if (req.geo.city === 'London') {
+ } else if (request.geo.city === 'London') {
return NextResponse.rewrite('/not-home')
}
-
return NextResponse.next()
}
```
All methods above return a `NextResponse` object that only takes effect if it's returned in the middleware function.
-`NextResponse` is fully typed and can be imported from `next/server`.
+`NextResponse` can be imported from `next/server`:
```typescript
import { NextResponse } from 'next/server'
```
-### Setting the cookie before a redirect
-
-In order to set the `cookie` _before_ a redirect, you can create an instance of `NextResponse`, then access the `cookie` method on the instance, before returning the response.
-
-Note that there is a [Chrome bug](https://bugs.chromium.org/p/chromium/issues/detail?id=696204) which means the entire redirect chain **must** be from the same origin, if they are from different origins, then the `cookie` might be missing until a refresh.
-
-```typescript
-import { NextResponse } from 'next/server'
-import type { NextRequest } from 'next/server'
-
-export function middleware(req: NextRequest) {
- const res = NextResponse.redirect('/') // creates an actual instance
- res.cookies.set('hello', 'world') // can be called on an instance
- return res
-}
-```
-
## userAgent
The `userAgent` helper allows you to interact with the user agent object from the request. It is abstracted from the native `Request` object, and is an opt in feature. It has the following properties:
-- `isBot`: Whether the request comes from a known bot
+- `isBot`: (`boolean`) Whether the request comes from a known bot
- `browser`
- - `name`: The name of the browser, or `undefined`
- - `version`: The version of the browser, determined dynamically, or `undefined`
+ - `name`: (`string || undefined`) The name of the browser
+ - `version`: (`string || undefined`) The version of the browser, determined dynamically
- `device`
- - `model`: The model of the device, determined dynamically, or `undefined`
- - `type`: The type of the browser, can be one of the following values: `console`, `mobile`, `tablet`, `smarttv`, `wearable`, `embedded`, or `undefined`
- - `vendor`: The vendor of the device, determined dynamically, or `undefined`
+ - `model`: (`string || undefined`) The model of the device, determined dynamically
+ - `type`: (`string || undefined`) The type of the browser, can be one of the following values: `console`, `mobile`, `tablet`, `smarttv`, `wearable`, `embedded`, or `undefined`
+ - `vendor`: (`string || undefined`) The vendor of the device, determined dynamically
- `engine`
- - `name`: The name of the browser engine, could be one of the following values: `Amaya`, `Blink`, `EdgeHTML`, `Flow`, `Gecko`, `Goanna`, `iCab`, `KHTML`, `Links`, `Lynx`, `NetFront`, `NetSurf`, `Presto`, `Tasman`, `Trident`, `w3m`, `WebKit` or `undefined`
- - `version`: The version of the browser engine, determined dynamically, or `undefined`
+ - `name`: (`string || undefined`) The name of the browser engine, could be one of the following values: `Amaya`, `Blink`, `EdgeHTML`, `Flow`, `Gecko`, `Goanna`, `iCab`, `KHTML`, `Links`, `Lynx`, `NetFront`, `NetSurf`, `Presto`, `Tasman`, `Trident`, `w3m`, `WebKit` or `undefined`
+ - `version`: (`string || undefined`) The version of the browser engine, determined dynamically, or `undefined`
- `os`
- - `name`: The name of the OS, could be `undefined`
- - `version`: The version of the OS, determined dynamically, or `undefined`
+ - `name`: (`string || undefined`) The name of the OS, could be `undefined`
+ - `version`: (`string || undefined`) The version of the OS, determined dynamically, or `undefined`
- `cpu`
- - `architecture`: The architecture of the CPU, could be one of the following values: `68k`, `amd64`, `arm`, `arm64`, `armhf`, `avr`, `ia32`, `ia64`, `irix`, `irix64`, `mips`, `mips64`, `pa-risc`, `ppc`, `sparc`, `sparc64` or `undefined`
+ - `architecture`: (`string || undefined`) The architecture of the CPU, could be one of the following values: `68k`, `amd64`, `arm`, `arm64`, `armhf`, `avr`, `ia32`, `ia64`, `irix`, `irix64`, `mips`, `mips64`, `pa-risc`, `ppc`, `sparc`, `sparc64` or `undefined`
-`userAgent` is fully typed and can be imported from `next/server`:
+`userAgent` can be imported from `next/server`:
```typescript
import { userAgent } from 'next/server'
@@ -177,7 +147,9 @@ export function middleware(request: NextRequest) {
}
```
-### Why does redirect use 307 and 308?
+## Common questions and answers
+
+### Why does `redirect` use 307 and 308?
When using `redirect()` you may notice that the status codes used are `307` for a temporary redirect, and `308` for a permanent redirect. While traditionally a `302` was used for a temporary redirect, and a `301` for a permanent redirect, many browsers changed the request method of the redirect, from a `POST` to `GET` request when using a `302`, regardless of the origins request method.
@@ -196,35 +168,13 @@ If you want to cause a `GET` response to a `POST` request, use `303`.
### How do I access Environment Variables?
-`process.env` can be used to access [Environment Variables](/docs/basic-features/environment-variables.md) from Middleware. These are evaluated at build time, so only environment variables _actually_ used will be included.
-
-Any variables in `process.env` must be accessed directly, and **cannot** be destructured:
-
-```typescript
-// Accessed directly, and not destructured works. process.env.NODE_ENV is `"development"` or `"production"`
-console.log(process.env.NODE_ENV)
-// This will not work
-const { NODE_ENV } = process.env
-// NODE_ENV is `undefined`
-console.log(NODE_ENV)
-// process.env is `{}`
-console.log(process.env)
-```
-
-### The body limitation
-
-When using middlewares, it is not permitted to change the response body: you can only set responses headers.
-Returning a body from a middleware function will issue an `500` server error with an explicit response message.
-
-The `NextResponse` API (which eventually is tweaking response headers) 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
+`process.env` can be used to access [Environment Variables](/docs/basic-features/environment-variables.md) from Edge Middleware. They are evaluated at build time.
-These are solid tools to implement cases such as A/B testing, authentication, feature flags, bot protection...
-A middleware with the ability to change the response's body would bypass Next.js routing logic.
+| Works | Does **not** work |
+| ------------------------------------------------------------ | ------------------------------------------ |
+| `console.log(process.env.MY_ENV_VARIABLE)` | `const getEnv = name => process.env[name]` |
+| `const { MY_ENV_VARIABLE } = process.env` | |
+| `const { "MY-ENV-VARIABLE": MY_ENV_VARIABLE } = process.env` | |
## Related
diff --git a/docs/api-routes/edge-api-routes.md b/docs/api-routes/edge-api-routes.md
new file mode 100644
index 0000000000000..f0ae729349509
--- /dev/null
+++ b/docs/api-routes/edge-api-routes.md
@@ -0,0 +1,70 @@
+---
+description: You can add the dynamic routes used for pages to API Routes too. Learn how it works here.
+---
+
+# Edge API Routes (Beta)
+
+
+ Examples
+
+
+
+API Routes can be configured to run at the Edge using an experimental flag. When deployed on Vercel, these API Routes run as [Edge Functions](https://vercel.com/docs/concepts/functions/vercel-edge-functions).
+
+Edge API Routes are similar to API Routes but with different infrastructure:
+
+- On every invocation API Routes are run from the same region
+- Edge API Routes are copied across the [Vercel Edge Network](https://vercel.com/docs/concepts/edge-network/overview), so on every invocation, the region that is closets to you will run the function, reducing latency massively
+
+Unlike API Routes, Edge API Routes:
+
+- Can stream responses
+- Run _after_ the cache
+- Can cache responses
+- Have zero cold starts
+
+## How to create an Edge API Route
+
+Edge API Routes have the same signature as [Edge Middleware](/docs/advanced-features/middleware), and support the same helpers from [`next/server`](/docs/api-reference/next/server). Note that the below examples use TypeScript, though this is **not** a requirement.
+
+### API Route
+
+```typescript
+import type { NextApiRequest, NextApiResponse } from 'next'
+
+export default (req: NextApiRequest, res: NextApiResponse) => {
+ res
+ .status(200)
+ .json({ name: `Hello, from ${req.url} I'm a Serverless Function'` })
+}
+```
+
+### Edge API Route
+
+```typescript
+import { NextResponse } from 'next/server'
+import type { NextRequest } from 'next/server'
+
+export default (req: NextRequest) => {
+ return new Response(`Hello, from ${req.url} I'm now an Edge API Route!`)
+}
+
+export const config = {
+ runtime: 'experimental-edge',
+}
+```
+
+## Trade-offs
+
+Edge API Routes are not suitable for all use cases:
+
+- Fetching data in an Edge API Routes from a location far away from its deployed location can add unwanted latency to the request
+- The maximum size for an Edge API Route is 1 MB, including all the code that is bundled in the function
+- **Native Node.js APIs are not supported**:
+ - ES modules `require()` is not allowed
+ - Libraries using Node.js APIs can't be used in Edge API Routes
+ - Dynamic code execution (such as `eval`) is not allowed
+
+For more information on limitations when using Edge API Routes, see the Vercel Edge Functions [Limitations documentation](https://vercel.com/docs/concepts/functions/vercel-edge-functions/limitations).
diff --git a/docs/manifest.json b/docs/manifest.json
index 841c77f26cf86..8ab903558c14a 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -4,7 +4,10 @@
"title": "Documentation",
"heading": true,
"routes": [
- { "title": "Getting Started", "path": "/docs/getting-started.md" },
+ {
+ "title": "Getting Started",
+ "path": "/docs/getting-started.md"
+ },
{
"title": "Basic Features",
"open": true,
@@ -139,6 +142,10 @@
{
"title": "Response Helpers",
"path": "/docs/api-routes/response-helpers.md"
+ },
+ {
+ "title": "Edge API Routes (Beta)",
+ "path": "/docs/api-routes/edge-api-routes.md"
}
]
},
@@ -271,7 +278,7 @@
"path": "/docs/advanced-features/measuring-performance.md"
},
{
- "title": "Middleware (Beta)",
+ "title": "Middleware",
"path": "/docs/advanced-features/middleware.md"
},
{
@@ -356,14 +363,20 @@
}
]
},
- { "title": "FAQ", "path": "/docs/faq.md" }
+ {
+ "title": "FAQ",
+ "path": "/docs/faq.md"
+ }
]
},
{
"title": "API Reference",
"heading": true,
"routes": [
- { "title": "CLI", "path": "/docs/api-reference/cli.md" },
+ {
+ "title": "CLI",
+ "path": "/docs/api-reference/cli.md"
+ },
{
"title": "Create Next App",
"path": "/docs/api-reference/create-next-app.md"