From 07a0700dcb33d77e8d5659be564b04db890384da Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Tue, 16 Apr 2024 22:52:53 -0500 Subject: [PATCH] docs: clarify runtime (#64593) --- .../01-routing/12-route-handlers.mdx | 14 --- .../04-edge-and-nodejs-runtimes.mdx | 88 ++++--------------- .../06-optimizing/04-metadata.mdx | 4 - .../06-optimizing/09-instrumentation.mdx | 2 +- .../01-metadata/app-icons.mdx | 25 ------ .../01-metadata/opengraph-image.mdx | 29 ------ .../route-segment-config.mdx | 37 ++------ docs/02-app/02-api-reference/07-edge.mdx | 7 +- errors/edge-dynamic-code-evaluation.mdx | 7 +- 9 files changed, 29 insertions(+), 184 deletions(-) diff --git a/docs/02-app/01-building-your-application/01-routing/12-route-handlers.mdx b/docs/02-app/01-building-your-application/01-routing/12-route-handlers.mdx index 122a4c620e..5e5d19eeef 100644 --- a/docs/02-app/01-building-your-application/01-routing/12-route-handlers.mdx +++ b/docs/02-app/01-building-your-application/01-routing/12-route-handlers.mdx @@ -406,8 +406,6 @@ const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }) -export const runtime = 'edge' - export async function POST(req: Request) { const { messages } = await req.json() const response = await openai.chat.completions.create({ @@ -430,8 +428,6 @@ const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }) -export const runtime = 'edge' - export async function POST(req) { const { messages } = await req.json() const response = await openai.chat.completions.create({ @@ -649,16 +645,6 @@ export async function POST(request) { Notably, unlike API Routes with the Pages Router, you do not need to use `bodyParser` to use any additional configuration. -### Edge and Node.js Runtimes - -Route Handlers have an isomorphic Web API to support both Edge and Node.js runtimes seamlessly, including support for streaming. Since Route Handlers use the same [route segment configuration](/docs/app/api-reference/file-conventions/route-segment-config) as Pages and Layouts, they support long-awaited features like general-purpose [statically regenerated](/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#revalidating-data) Route Handlers. - -You can use the `runtime` segment config option to specify the runtime: - -```ts -export const runtime = 'edge' // 'nodejs' is the default -``` - ### Non-UI Responses You can use Route Handlers to return non-UI content. Note that [`sitemap.xml`](/docs/app/api-reference/file-conventions/metadata/sitemap#generating-a-sitemap-using-code-js-ts), [`robots.txt`](/docs/app/api-reference/file-conventions/metadata/robots#generate-a-robots-file), [`app icons`](/docs/app/api-reference/file-conventions/metadata/app-icons#generate-icons-using-code-js-ts-tsx), and [open graph images](/docs/app/api-reference/file-conventions/metadata/opengraph-image) all have built-in support. diff --git a/docs/02-app/01-building-your-application/03-rendering/04-edge-and-nodejs-runtimes.mdx b/docs/02-app/01-building-your-application/03-rendering/04-edge-and-nodejs-runtimes.mdx index ededec85c1..7b858831e8 100644 --- a/docs/02-app/01-building-your-application/03-rendering/04-edge-and-nodejs-runtimes.mdx +++ b/docs/02-app/01-building-your-application/03-rendering/04-edge-and-nodejs-runtimes.mdx @@ -1,84 +1,26 @@ --- -title: Edge and Node.js Runtimes +title: Runtimes description: Learn about the switchable runtimes (Edge and Node.js) in Next.js. +related: + description: View the Edge Runtime API reference. + links: + - app/api-reference/edge --- {/* The content of this doc is shared between the app and pages router. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} -In the context of Next.js, runtime refers to the set of libraries, APIs, and general functionality available to your code during execution. +Next.js has two server runtimes you can use in your application: -On the server, there are two runtimes where parts of your application code can be rendered: +- The **Node.js Runtime** (default) which has access to all Node.js APIs and compatible packages from the ecosystem. +- The **Edge Runtime** which contains a more limited [set of APIs](/docs/app/api-reference/edge). -- The **Node.js Runtime** (default) has access to all Node.js APIs and compatible packages from the ecosystem. -- The **Edge Runtime** is based on [Web APIs](/docs/app/api-reference/edge). +## Use Cases -## Runtime Differences +- The Node.js runtime is used for rendering your application. +- The Edge runtime is used for Middleware (routing rules like redirects, rewrites, and setting headers). -There are many considerations to make when choosing a runtime. This table shows the major differences at a glance. If you want a more in-depth analysis of the differences, check out the sections below. +## Caveats -| | Node | Serverless | Edge | -| ------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- | ---------------- | -| Cold Boot | / | Normal | Low | -| [HTTP Streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming) | Yes | Yes | Yes | -| IO | All | All | `fetch` | -| Scalability | / | High | Highest | -| Security | Normal | High | High | -| Latency | Normal | Low | Lowest | -| npm Packages | All | All | A smaller subset | -| [Static Rendering](/docs/app/building-your-application/rendering/server-components#static-rendering-default) | Yes | Yes | No | -| [Dynamic Rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) | Yes | Yes | Yes | -| [Data Revalidation w/ `fetch`](/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#revalidating-data) | Yes | Yes | Yes | - -### Edge Runtime - -In Next.js, the lightweight Edge Runtime is a subset of available Node.js APIs. - -The Edge Runtime is ideal if you need to deliver dynamic, personalized content at low latency with small, simple functions. The Edge Runtime's speed comes from its minimal use of resources, but that can be limiting in many scenarios. - -For example, code executed in the Edge Runtime [on Vercel cannot exceed between 1 MB and 4 MB](https://vercel.com/docs/concepts/limits/overview#edge-middleware-and-edge-functions-size), this limit includes imported packages, fonts and files, and will vary depending on your deployment infrastructure. In addition, the Edge Runtime does not support all Node.js APIs meaning some `npm` packages may not work. For example, "Module not found: Can't resolve 'fs'" or similar errors. We recommend using the Node.js runtime if you need to use these APIs or packages. - -### Node.js Runtime - -Using the Node.js runtime gives you access to all Node.js APIs, and all npm packages that rely on them. However, it's not as fast to start up as routes using the Edge runtime. - -Deploying your Next.js application to a Node.js server will require managing, scaling, and configuring your infrastructure. Alternatively, you can consider deploying your Next.js application to a serverless platform like Vercel, which will handle this for you. - -### Serverless Node.js - -Serverless is ideal if you need a scalable solution that can handle more complex computational loads than the Edge Runtime. With Serverless Functions on Vercel, for example, your overall code size is [50MB](https://vercel.com/docs/concepts/limits/overview#serverless-function-size) including imported packages, fonts, and files. - -The downside compared to routes using the [Edge](https://vercel.com/docs/concepts/functions/edge-functions) is that it can take hundreds of milliseconds for Serverless Functions to boot up before they begin processing requests. Depending on the amount of traffic your site receives, this could be a frequent occurrence as the functions are not frequently "warm". - - - -## Examples - -### Segment Runtime Option - -You can specify a runtime for individual route segments in your Next.js application. To do so, [declare a variable called `runtime` and export it](/docs/app/api-reference/file-conventions/route-segment-config). The variable must be a string, and must have a value of either `'nodejs'` or `'edge'` runtime. - -The following example demonstrates a page route segment that exports a `runtime` with a value of `'edge'`: - -```tsx filename="app/page.tsx" switcher -export const runtime = 'edge' // 'nodejs' (default) | 'edge' -``` - -```jsx filename="app/page.js" switcher -export const runtime = 'edge' // 'nodejs' (default) | 'edge' -``` - -You can also define `runtime` on a layout level, which will make all routes under the layout run on the edge runtime: - -```tsx filename="app/layout.tsx" switcher -export const runtime = 'edge' // 'nodejs' (default) | 'edge' -``` - -```jsx filename="app/layout.js" switcher -export const runtime = 'edge' // 'nodejs' (default) | 'edge' -``` - -If the segment runtime is _not_ set, the default `nodejs` runtime will be used. You do not need to use the `runtime` option if you do not plan to change from the Node.js runtime. - - - -> Please refer to the [Node.js Docs](https://nodejs.org/docs/latest/api/) and [Edge Docs](/docs/app/api-reference/edge) for the full list of available APIs. Both runtimes can also support [streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming) depending on your deployment infrastructure. +- The Edge runtime does not support all Node.js APIs. Some packages will not work. Learn more about the unsupported APIs in the [Edge Runtime](/docs/app/api-reference/edge#unsupported-apis). +- The Edge runtime does not support Incremental Static Regeneration (ISR). +- Both runtimes can support [streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming) depending on your deployment infrastructure. diff --git a/docs/02-app/01-building-your-application/06-optimizing/04-metadata.mdx b/docs/02-app/01-building-your-application/06-optimizing/04-metadata.mdx index e6bb31efd2..4648804555 100644 --- a/docs/02-app/01-building-your-application/06-optimizing/04-metadata.mdx +++ b/docs/02-app/01-building-your-application/06-optimizing/04-metadata.mdx @@ -247,15 +247,11 @@ export const metadata = { The `ImageResponse` constructor allows you to generate dynamic images using JSX and CSS. This is useful for creating social media images such as Open Graph images, Twitter cards, and more. -`ImageResponse` uses the [Edge Runtime](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#edge-runtime), and Next.js automatically adds the correct headers to cached images at the edge, helping improve performance and reducing recomputation. - To use it, you can import `ImageResponse` from `next/og`: ```jsx filename="app/about/route.js" import { ImageResponse } from 'next/og' -export const runtime = 'edge' - export async function GET() { return new ImageResponse( ( diff --git a/docs/02-app/01-building-your-application/06-optimizing/09-instrumentation.mdx b/docs/02-app/01-building-your-application/06-optimizing/09-instrumentation.mdx index 901ea85aea..a6e3257743 100644 --- a/docs/02-app/01-building-your-application/06-optimizing/09-instrumentation.mdx +++ b/docs/02-app/01-building-your-application/06-optimizing/09-instrumentation.mdx @@ -70,7 +70,7 @@ export async function register() { ### Importing runtime-specific code -Next.js calls `register` in all environments, so it's important to conditionally import any code that doesn't support specific runtimes (e.g. [Edge](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#edge-runtime) or [Node.js](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#nodejs-runtime)). You can use the `NEXT_RUNTIME` environment variable to get the current environment: +Next.js calls `register` in all environments, so it's important to conditionally import any code that doesn't support specific runtimes (e.g. [Edge or Node.js](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes)). You can use the `NEXT_RUNTIME` environment variable to get the current environment: ```ts filename="instrumentation.ts" switcher export async function register() { diff --git a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/app-icons.mdx b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/app-icons.mdx index b55321180c..c3137be5bd 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/app-icons.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/app-icons.mdx @@ -83,9 +83,6 @@ The easiest way to generate an icon is to use the [`ImageResponse`](/docs/app/ap ```tsx filename="app/icon.tsx" switcher import { ImageResponse } from 'next/og' -// Route segment config -export const runtime = 'edge' - // Image metadata export const size = { width: 32, @@ -126,9 +123,6 @@ export default function Icon() { ```jsx filename="app/icon.js" switcher import { ImageResponse } from 'next/og' -// Route segment config -export const runtime = 'edge' - // Image metadata export const size = { width: 32, @@ -258,25 +252,6 @@ export default function Icon() {} `icon` and `apple-icon` are specialized [Route Handlers](/docs/app/building-your-application/routing/route-handlers) that can use the same [route segment configuration](/docs/app/api-reference/file-conventions/route-segment-config) options as Pages and Layouts. -| Option | Type | Default | -| -------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---------- | -| [`dynamic`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) | `'auto' \| 'force-dynamic' \| 'error' \| 'force-static'` | `'auto'` | -| [`revalidate`](/docs/app/api-reference/file-conventions/route-segment-config#revalidate) | `false \| 'force-cache' \| 0 \| number` | `false` | -| [`runtime`](/docs/app/api-reference/file-conventions/route-segment-config#runtime) | `'nodejs' \| 'edge'` | `'nodejs'` | -| [`preferredRegion`](/docs/app/api-reference/file-conventions/route-segment-config#preferredregion) | `'auto' \| 'global' \| 'home' \| string \| string[]` | `'auto'` | - -```tsx filename="app/icon.tsx" switcher -export const runtime = 'edge' - -export default function Icon() {} -``` - -```jsx filename="app/icon.js" switcher -export const runtime = 'edge' - -export default function Icon() {} -``` - ## Version History | Version | Changes | diff --git a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/opengraph-image.mdx b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/opengraph-image.mdx index fb6a439454..9905874b54 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/opengraph-image.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/opengraph-image.mdx @@ -92,9 +92,6 @@ The easiest way to generate an image is to use the [ImageResponse](/docs/app/api ```tsx filename="app/about/opengraph-image.tsx" switcher import { ImageResponse } from 'next/og' -// Route segment config -export const runtime = 'edge' - // Image metadata export const alt = 'About Acme' export const size = { @@ -149,9 +146,6 @@ export default async function Image() { ```jsx filename="app/about/opengraph-image.js" switcher import { ImageResponse } from 'next/og' -// Route segment config -export const runtime = 'edge' - // Image metadata export const alt = 'About Acme' export const size = { @@ -313,25 +307,6 @@ export default function Image() {} `opengraph-image` and `twitter-image` are specialized [Route Handlers](/docs/app/building-your-application/routing/route-handlers) that can use the same [route segment configuration](/docs/app/api-reference/file-conventions/route-segment-config) options as Pages and Layouts. -| Option | Type | Default | -| -------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---------- | -| [`dynamic`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) | `'auto' \| 'force-dynamic' \| 'error' \| 'force-static'` | `'auto'` | -| [`revalidate`](/docs/app/api-reference/file-conventions/route-segment-config#revalidate) | `false \| 'force-cache' \| 0 \| number` | `false` | -| [`runtime`](/docs/app/api-reference/file-conventions/route-segment-config#runtime) | `'nodejs' \| 'edge'` | `'nodejs'` | -| [`preferredRegion`](/docs/app/api-reference/file-conventions/route-segment-config#preferredregion) | `'auto' \| 'global' \| 'home' \| string \| string[]` | `'auto'` | - -```tsx filename="app/opengraph-image.tsx" switcher -export const runtime = 'edge' - -export default function Image() {} -``` - -```jsx filename="app/opengraph-image.js" switcher -export const runtime = 'edge' - -export default function Image() {} -``` - ### Examples #### Using external data @@ -344,8 +319,6 @@ This example uses the `params` object and external data to generate the image. ```tsx filename="app/posts/[slug]/opengraph-image.tsx" switcher import { ImageResponse } from 'next/og' -export const runtime = 'edge' - export const alt = 'About Acme' export const size = { width: 1200, @@ -384,8 +357,6 @@ export default async function Image({ params }: { params: { slug: string } }) { ```jsx filename="app/posts/[slug]/opengraph-image.js" switcher import { ImageResponse } from 'next/og' -export const runtime = 'edge' - export const alt = 'About Acme' export const size = { width: 1200, diff --git a/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx b/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx index 048ed09ec1..42bbade1e1 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx @@ -15,34 +15,6 @@ The Route Segment options allows you to configure the behavior of a [Page](/docs | [`preferredRegion`](#preferredregion) | `'auto' \| 'global' \| 'home' \| string \| string[]` | `'auto'` | | [`maxDuration`](#maxduration) | `number` | Set by deployment platform | -```tsx filename="layout.tsx | page.tsx | route.ts" switcher -export const dynamic = 'auto' -export const dynamicParams = true -export const revalidate = false -export const fetchCache = 'auto' -export const runtime = 'nodejs' -export const preferredRegion = 'auto' -export const maxDuration = 5 - -export default function MyComponent() {} -``` - -```jsx filename="layout.js | page.js | route.js" switcher -export const dynamic = 'auto' -export const dynamicParams = true -export const revalidate = false -export const fetchCache = 'auto' -export const runtime = 'nodejs' -export const preferredRegion = 'auto' -export const maxDuration = 5 - -export default function MyComponent() {} -``` - -> **Good to know**: -> -> - The values of the config options currently need be statically analyzable. For example `revalidate = 600` is valid, but `revalidate = 60 * 10` is not. - ## Options ### `dynamic` @@ -118,7 +90,10 @@ export const revalidate = false - **`0`**: Ensure a layout or page is always [dynamically rendered](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) even if no dynamic functions or uncached data fetches are discovered. This option changes the default of `fetch` requests that do not set a `cache` option to `'no-store'` but leaves `fetch` requests that opt into `'force-cache'` or use a positive `revalidate` as is. - **`number`**: (in seconds) Set the default revalidation frequency of a layout or page to `n` seconds. -> **Good to know**: The `revalidate` option is only available when using the [Node.js Runtime](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#nodejs-runtime). This means using the `revalidate` option with `runtime = 'edge'` will not work. +> **Good to know**: +> +> - The revalidate value needs to be statically analyzable. For example `revalidate = 600` is valid, but `revalidate = 60 * 10` is not. +> - The revalidate value is not available when using `runtime = 'edge'`. #### Revalidation Frequency @@ -168,6 +143,8 @@ export const fetchCache = 'auto' ### `runtime` +We recommend using the Node.js runtime for rendering your application, and the Edge runtime for Middleware (only supported option). + ```tsx filename="layout.tsx | page.tsx | route.ts" switcher export const runtime = 'nodejs' // 'nodejs' | 'edge' @@ -181,7 +158,7 @@ export const runtime = 'nodejs' - **`'nodejs'`** (default) - **`'edge'`** -Learn more about the [Edge and Node.js runtimes](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). +Learn more about the [different runtimes](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). ### `preferredRegion` diff --git a/docs/02-app/02-api-reference/07-edge.mdx b/docs/02-app/02-api-reference/07-edge.mdx index 3bed7bfb2b..c4a7f0fc27 100644 --- a/docs/02-app/02-api-reference/07-edge.mdx +++ b/docs/02-app/02-api-reference/07-edge.mdx @@ -5,7 +5,7 @@ description: API Reference for the Edge Runtime. {/* The content of this doc is shared between the app and pages router. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} -The Next.js Edge Runtime is based on standard Web APIs, it supports the following APIs: +The Next.js Edge Runtime is used for Middleware and supports the following APIs: ## Network APIs @@ -146,11 +146,10 @@ The following JavaScript language features are disabled, and **will not work:** | [`WebAssembly.instantiate`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiate) | Compiles and instantiates a WebAssembly module from a buffer source | In rare cases, your code could contain (or import) some dynamic code evaluation statements which _can not be reached at runtime_ and which can not be removed by treeshaking. -You can relax the check to allow specific files with your Middleware or Edge API Route exported configuration: +You can relax the check to allow specific files with your Middleware configuration: -```javascript +```javascript filename="middleware.ts" export const config = { - runtime: 'edge', // for Edge API Routes only unstable_allowDynamic: [ // allows a single file '/lib/utilities.js', diff --git a/errors/edge-dynamic-code-evaluation.mdx b/errors/edge-dynamic-code-evaluation.mdx index bb842f4181..68d60f62a3 100644 --- a/errors/edge-dynamic-code-evaluation.mdx +++ b/errors/edge-dynamic-code-evaluation.mdx @@ -1,10 +1,10 @@ --- -title: Dynamic code evaluation is not available in Middlewares or Edge API Routes +title: Dynamic code evaluation is not available in Middleware --- ## Why This Error Occurred -`eval()`, `new Function()` or compiling WASM binaries dynamically is not allowed in Middlewares or Edge API Routes. +`eval()`, `new Function()` or compiling WASM binaries dynamically is not allowed in Middleware. Specifically, the following APIs are not supported: - `eval()` @@ -31,11 +31,10 @@ export default async function middleware() { ``` In rare cases, your code could contain (or import) some dynamic code evaluation statements which _can not be reached at runtime_ and which can not be removed by tree-shaking. -You can relax the check to allow specific files with your Middleware or Edge API Route exported [configuration](/docs/pages/api-reference/edge#unsupported-apis): +You can relax the check to allow specific files with your Middleware [configuration](/docs/pages/api-reference/edge#unsupported-apis): ```tsx filename="pages/api/example.ts" export const config = { - runtime: 'edge', // for Edge API Routes only unstable_allowDynamic: [ '/lib/utilities.js', // allows a single file '/node_modules/function-bind/**', // use a glob to allow anything in the function-bind 3rd party module