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. -
- - Middleware API Reference - Learn more about the supported APIs for Middleware. - -
+See the [`next/server`](/docs/api-reference/next/server.md) documentation for more information. + +## Related
- + Edge Runtime - Learn more about the supported Web APIs available. + Learn more about the supported APIs for Next.js Middleware.
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"