adapterPath
Next.js provides a built-in adapters API. It allows deployment platforms or build systems to integrate with the Next.js build process.
For a full reference implementation, see the nextjs/adapter-vercel adapter.
Configuration
To use an adapter, specify the path to your adapter module in adapterPath:
/** @type {import('next').NextConfig} */
const nextConfig = {
adapterPath: require.resolve('./my-adapter.js'),
}
module.exports = nextConfigAlternatively NEXT_ADAPTER_PATH can be set to enable zero-config usage in deployment platforms.
Creating an Adapter
An adapter is a module that exports an object implementing the NextAdapter interface.
The interface can be imported from the next package:
import type { NextAdapter } from 'next'The interface is defined as follows:
type Route = {
source?: string
sourceRegex: string
destination?: string
headers?: Record<string, string>
has?: RouteHas[]
missing?: RouteHas[]
status?: number
priority?: boolean
}
export interface AdapterOutputs {
pages: Array<AdapterOutput['PAGES']>
middleware?: AdapterOutput['MIDDLEWARE']
appPages: Array<AdapterOutput['APP_PAGE']>
pagesApi: Array<AdapterOutput['PAGES_API']>
appRoutes: Array<AdapterOutput['APP_ROUTE']>
prerenders: Array<AdapterOutput['PRERENDER']>
staticFiles: Array<AdapterOutput['STATIC_FILE']>
}
export interface NextAdapter {
name: string
modifyConfig?: (
config: NextConfigComplete,
ctx: {
phase: PHASE_TYPE
nextVersion: string
}
) => Promise<NextConfigComplete> | NextConfigComplete
onBuildComplete?: (ctx: {
routing: {
beforeMiddleware: Array<Route>
beforeFiles: Array<Route>
afterFiles: Array<Route>
dynamicRoutes: Array<Route>
onMatch: Array<Route>
fallback: Array<Route>
shouldNormalizeNextData: boolean
rsc: RoutesManifest['rsc']
}
outputs: AdapterOutputs
projectDir: string
repoRoot: string
distDir: string
config: NextConfigComplete
nextVersion: string
buildId: string
}) => Promise<void> | void
}Basic Adapter Structure
Here's a minimal adapter example:
/** @type {import('next').NextAdapter} */
const adapter = {
name: 'my-custom-adapter',
async modifyConfig(config, { phase }) {
// Modify the Next.js config based on the build phase
if (phase === 'phase-production-build') {
return {
...config,
// Add your modifications
}
}
return config
},
async onBuildComplete({
routing,
outputs,
projectDir,
repoRoot,
distDir,
config,
nextVersion,
buildId,
}) {
// Process the build output
console.log('Build completed with', outputs.pages.length, 'pages')
console.log('Build ID:', buildId)
console.log('Dynamic routes:', routing.dynamicRoutes.length)
// Access emitted output entries
for (const page of outputs.pages) {
console.log('Page:', page.pathname, 'at', page.filePath)
}
for (const apiRoute of outputs.pagesApi) {
console.log('API Route:', apiRoute.pathname, 'at', apiRoute.filePath)
}
for (const appPage of outputs.appPages) {
console.log('App Page:', appPage.pathname, 'at', appPage.filePath)
}
for (const prerender of outputs.prerenders) {
console.log('Prerendered:', prerender.pathname)
}
},
}
module.exports = adapterAPI Reference
async modifyConfig(config, context)
Called for any CLI command that loads the next.config.js file to allow modification of the configuration.
Parameters:
config: The complete Next.js configuration objectcontext.phase: The current build phase (see phases)context.nextVersion: Version of Next.js being used
Returns: The modified configuration object (can be async)
async onBuildComplete(context)
Called after the build process completes with detailed information about routes and outputs.
Parameters:
context.routing: Object containing Next.js routing phases and metadatarouting.beforeMiddleware: Routes executed before middleware (includes header and redirect handling)routing.beforeFiles: Rewrite routes checked before filesystem route matchingrouting.afterFiles: Rewrite routes checked after filesystem route matchingrouting.dynamicRoutes: Dynamic route matching tablerouting.onMatch: Routes applied after a successful match (for example immutable static asset cache headers)routing.fallback: Final rewrite fallback routesrouting.shouldNormalizeNextData: Whether/_next/data/<buildId>/...URLs should be normalized during matchingrouting.rsc: Route metadata used for React Server Components routing behavior
context.outputs: Detailed information about all build outputs organized by typecontext.projectDir: Absolute path to the Next.js project directorycontext.repoRoot: Absolute path to the detected repository rootcontext.distDir: Absolute path to the build output directorycontext.config: The final Next.js configuration (withmodifyConfigapplied)context.nextVersion: Version of Next.js being usedcontext.buildId: Unique identifier for the current build
Testing Adapters
Next.js provides a test harness for validating adapters. Running the end-to-end tests for deployment.
Example GitHub Actions workflow:
name: test-e2e-deploy
on:
workflow_dispatch:
inputs:
nextjsRef:
description: 'Next.js repo ref (branch/tag/SHA)'
default: 'canary'
type: string
# schedule:
# - cron: '0 2 * * *'
jobs:
build:
name: Build Next.js + adapter
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
path: adapter
- uses: actions/checkout@v4
with:
repository: vercel/next.js
ref: ${{ inputs.nextjsRef || 'canary' }}
path: nextjs
fetch-depth: 25
- uses: actions/setup-node@v4
with: { node-version: '20' }
- name: Setup pnpm
run: npm i -g corepack@0.31 && corepack enable
- name: Install & build Next.js
working-directory: nextjs
run: pnpm install && pnpm build && pnpm install
- name: Install Playwright
working-directory: nextjs
run: pnpm playwright install --with-deps chromium
- name: Build adapter
working-directory: adapter
run: pnpm install && pnpm build
- uses: actions/cache/save@v4
with:
path: |
nextjs
adapter
~/.cache/ms-playwright
key: build-${{ github.sha }}-${{ github.run_id }}
test:
name: Tests (${{ matrix.group }})
needs: build
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
group:
[
1/16,
2/16,
3/16,
4/16,
5/16,
6/16,
7/16,
8/16,
9/16,
10/16,
11/16,
12/16,
13/16,
14/16,
15/16,
16/16,
]
steps:
- uses: actions/cache/restore@v4
with:
path: |
nextjs
adapter
~/.cache/ms-playwright
key: build-${{ github.sha }}-${{ github.run_id }}
- uses: actions/setup-node@v4
with: { node-version: '20' }
- name: Setup pnpm
run: npm i -g corepack@0.31 && corepack enable
- name: Ensure Playwright browser
working-directory: nextjs
run: pnpm playwright install chromium
- name: Make scripts executable
run: chmod +x adapter/scripts/e2e-deploy.sh
adapter/scripts/e2e-logs.sh
adapter/scripts/e2e-cleanup.sh
- name: Run deploy tests
working-directory: nextjs
env:
NEXT_TEST_MODE: deploy
NEXT_E2E_TEST_TIMEOUT: 240000
NEXT_EXTERNAL_TESTS_FILTERS: test/deploy-tests-manifest.json
ADAPTER_DIR: ${{ github.workspace }}/adapter
IS_TURBOPACK_TEST: 1
NEXT_TEST_JOB: 1
NEXT_TELEMETRY_DISABLED: 1
# Change these to your adapter's scripts
# Keep as-is if the scripts are in the adapter repository `scripts` directory
NEXT_TEST_DEPLOY_SCRIPT_PATH: ${{ github.workspace }}/adapter/scripts/e2e-deploy.sh
NEXT_TEST_DEPLOY_LOGS_SCRIPT_PATH: ${{ github.workspace }}/adapter/scripts/e2e-logs.sh
NEXT_TEST_CLEANUP_SCRIPT_PATH: ${{ github.workspace }}/adapter/scripts/e2e-cleanup.sh
run: node run-tests.js --timings -g ${{ matrix.group }} -c 2 --type e2eThe test harness looks for these environment variables:
NEXT_TEST_DEPLOY_SCRIPT_PATH: Path to the executable that builds and deploys the isolated test appNEXT_TEST_DEPLOY_LOGS_SCRIPT_PATH: Path to the executable that returns build and runtime logs for that deploymentNEXT_TEST_CLEANUP_SCRIPT_PATH: Path to the optional executable that tears the deployment down after the test run
Custom deploy script contract
The deploy script NEXT_TEST_DEPLOY_SCRIPT_PATH is executed with cwd set to the isolated temporary app created by the Next.js test harness.
The deploy script must follow this contract:
- Exit with a non-zero code on failure.
- Print the deployment URL to
stdout. This will be used to verify the deployment. Avoid writing anything else tostdout. - Write diagnostic output to
stderror to files inside the working directory.
Because the deploy script and logs script run as separate processes, any data you want to use later, such as build IDs or server logs, should be persisted to files inside the working directory.
Example deploy script:
#!/usr/bin/env bash
set -euo pipefail
# Install the adapter, build the app, and deploy or start it.
node -e "
const pkg=JSON.parse(require('fs').readFileSync('package.json','utf8'));
pkg.dependencies=pkg.dependencies||{};
pkg.dependencies['adapter']='file:${ADAPTER_DIR}';
require('fs').writeFileSync('package.json',JSON.stringify(pkg,null,2));
" >&2
# Set the adapter path so that the app uses it.
export NEXT_ADAPTER_PATH="${ADAPTER_DIR}/dist/index.js"
# Write any metadata needed later to files in the working directory.
BUILD_ID="$(cat .next/BUILD_ID)"
DEPLOYMENT_ID="my-adapter-local"
# If your adapter generates an immutable asset token, set it here.
# Otherwise use "undefined" to indicate there is none.
IMMUTABLE_ASSET_TOKEN="undefined"
{
echo "BUILD_ID: $BUILD_ID"
echo "DEPLOYMENT_ID: $DEPLOYMENT_ID"
echo "IMMUTABLE_ASSET_TOKEN: $IMMUTABLE_ASSET_TOKEN"
} >> .adapter-build.log
# Build the app
pnpm build
# Start or deploy the app. Capture the URL at this point or make the script output the URL to stdout.
provider-cli-to-deploy
# Example URL output:
# echo "http://127.0.0.1:3000"Custom logs script contract
The logs script NEXT_TEST_DEPLOY_LOGS_SCRIPT_PATH is executed with cwd set to the isolated temporary app created by the Next.js test harness.
Additionally it receives NEXT_TEST_DIR and NEXT_TEST_DEPLOY_URL as environment variables.
Its output must include lines starting with:
BUILD_ID:DEPLOYMENT_ID:IMMUTABLE_ASSET_TOKEN:(use the valueundefinedif your adapter does not produce one)
After those markers, the logs script can print any additional build or server logs that would help debug failures.
#!/usr/bin/env bash
set -euo pipefail
if [ -f ".adapter-build.log" ]; then
cat ".adapter-build.log"
fi
if [ -f ".adapter-server.log" ]; then
echo "=== .adapter-server.log ==="
cat ".adapter-server.log"
fiOne pattern is to have the deploy script write .adapter-build.log and .adapter-server.log, then have the logs script replay those files so the harness can extract the required markers. This is one option, each platform has different ways to get the logs.
Custom cleanup script contract
The cleanup script NEXT_TEST_CLEANUP_SCRIPT_PATH is executed with cwd set to the isolated temporary app created by the Next.js test harness.
Additionally it receives NEXT_TEST_DIR and NEXT_TEST_DEPLOY_URL as environment variables.
The cleanup script can be used to clean up any resources created by the deploy script. It runs after the tests have completed.
Routing with @next/routing
You can use @next/routing to reproduce Next.js route matching behavior with data from onBuildComplete.
@next/routing is experimental and will stabilize with the adapters API.
import { resolveRoutes } from '@next/routing'
const pathnames = [
...outputs.pages,
...outputs.pagesApi,
...outputs.appPages,
...outputs.appRoutes,
...outputs.staticFiles,
].map((output) => output.pathname)
const result = await resolveRoutes({
url: requestUrl,
buildId,
basePath: config.basePath || '',
i18n: config.i18n,
headers: requestHeaders,
requestBody,
pathnames,
routes: routing,
invokeMiddleware: async (ctx) => {
// platform-specific middleware invocation
return {}
},
})
if (result.resolvedPathname) {
console.log('Resolved pathname:', result.resolvedPathname)
console.log('Resolved query:', result.resolvedQuery)
console.log('Invocation target:', result.invocationTarget)
}resolveRoutes() returns:
resolvedPathname: The route pathname selected by Next.js routing. For dynamic routes, this is the matched route template such as/blog/[slug].resolvedQuery: The final query after rewrites or middleware have added or replaced search params.invocationTarget: The concrete pathname and query to invoke for the matched route.
For example, if /blog/post-1?draft=1 matches /blog/[slug]?slug=post-1, resolvedPathname is /blog/[slug] while invocationTarget.pathname is /blog/post-1.
Implementing PPR in an Adapter
For partially prerendered app routes, onBuildComplete gives you the data needed to seed and resume PPR:
outputs.prerenders[].fallback.filePath: path to the generated fallback shell (for example HTML)outputs.prerenders[].fallback.postponedState: serialized postponed state used to resume rendering
1. Seed shell + postponed state at build time
import { readFile } from 'node:fs/promises'
async function seedPprEntries(outputs: AdapterOutputs) {
for (const prerender of outputs.prerenders) {
const fallback = prerender.fallback
if (!fallback?.filePath || !fallback.postponedState) continue
const shell = await readFile(fallback.filePath, 'utf8')
await platformCache.set(prerender.pathname, {
shell,
postponedState: fallback.postponedState,
initialHeaders: fallback.initialHeaders,
initialStatus: fallback.initialStatus,
initialRevalidate: fallback.initialRevalidate,
initialExpiration: fallback.initialExpiration,
})
}
}2. Runtime flow: serve cached shell and resume in background
At request time, you can stream a single response that is the concatenation of:
- cached HTML shell stream
- resumed render stream (generated after invoking
handlerwith postponed state)
Client
| GET /ppr-route
v
Adapter Router
|
|-- read cached shell + postponedState ---> Platform Cache
|<------------- cache hit -----------------|
|
|-- create responseStream = concat(shellStream, resumedStream)
|
|-- start piping shellStream ------------> Client (first bytes)
|
|-- invoke handler(req, res, { requestMeta: { postponed } })
| -------------------------------------> Entrypoint (handler)
| <------------------------------------- resumed chunks/cache entry
|
|-- append resumed chunks to resumedStream
|
'-- client receives one HTTP response:
[shell bytes........][resumed bytes........]3. Update cache with requestMeta.onCacheEntryV2
requestMeta.onCacheEntryV2 is called when a response cache entry is looked up or generated. Use it to persist updated shell/postponed data.
requestMeta.onCacheEntrystill works, but is deprecated.- Prefer
requestMeta.onCacheEntryV2. - If your adapter uses an internal
onCacheCallbackabstraction, wire it torequestMeta.onCacheEntryV2.
await handler(req, res, {
waitUntil,
requestMeta: {
postponed: cachedPprEntry?.postponedState,
onCacheEntryV2: async (cacheEntry, meta) => {
if (cacheEntry.value?.kind === 'APP_PAGE') {
const html =
cacheEntry.value.html &&
typeof cacheEntry.value.html.toUnchunkedString === 'function'
? cacheEntry.value.html.toUnchunkedString()
: null
await platformCache.set(meta.url || req.url || '/', {
shell: html,
postponedState: cacheEntry.value.postponed,
headers: cacheEntry.value.headers,
status: cacheEntry.value.status,
cacheControl: cacheEntry.cacheControl,
})
}
// Return true only if your adapter already wrote the response itself.
return false
},
},
})Entrypoint (handler)
| onCacheEntryV2(cacheEntry, { url })
v
requestMeta.onCacheEntryV2 callback
|
|-- if APP_PAGE ---> persist html + postponedState + headers ---> Platform Cache
|
'-- return false: continue normal Next.js response flow
return true: adapter already handled response (short-circuit)Invoking Entrypoints
Build output entrypoints use a handler(..., ctx) interface, with runtime-specific request/response types.
Node.js runtime (runtime: 'nodejs')
Node.js entrypoints use the following interface:
handler(
req: IncomingMessage,
res: ServerResponse,
ctx: {
waitUntil?: (promise: Promise<void>) => void
requestMeta?: RequestMeta
}
): Promise<void>When invoking Node.js entrypoints directly, adapters can pass helpers directly on requestMeta instead of relying on internals. Some of the supported fields are hostname,
revalidate, and render404:
await handler(req, res, {
requestMeta: {
// Relative path from process.cwd() to the Next.js project directory.
relativeProjectDir: '.',
// Optional hostname used by route handlers when constructing absolute URLs.
hostname: '127.0.0.1',
// Optional internal revalidate function to avoid revalidating over the network
revalidate: async ({ urlPath, headers, opts }) => {
// platform-specific revalidate implementation
},
// Optional function to render the 404 page for pages router `notFound: true`
render404: async (req, res, parsedUrl, setHeaders) => {
// platform-specific 404 rendering implementation
},
},
})Relevant files in the Next.js core:
packages/next/src/build/templates/app-page.tspackages/next/src/build/templates/app-route.ts- and
packages/next/src/build/templates/pages-api.ts
Edge runtime (runtime: 'edge')
Edge entrypoints use the following interface:
handler(
request: Request,
ctx: {
waitUntil?: (prom: Promise<void>) => void
signal?: AbortSignal
requestMeta?: RequestMeta
}
): Promise<Response>The shape is aligned around handler(..., ctx), but Node.js and Edge runtimes use different request/response primitives.
For outputs with runtime: 'edge', Next.js also provides output.edgeRuntime with the canonical metadata needed to invoke the entrypoint:
{
modulePath: string // Absolute path to the module registered in the edge runtime
entryKey: string // Canonical key used by the edge entry registry
handlerExport: string // Export name to invoke, currently 'handler'
}After your edge runtime loads and evaluates the chunks for modulePath, use entryKey to read the registered entry from the global edge entry registry (globalThis._ENTRIES), then invoke handlerExport from that entry:
const entry = await globalThis._ENTRIES[output.edgeRuntime.entryKey]
const handler = entry[output.edgeRuntime.handlerExport]
await handler(request, ctx)Use edgeRuntime instead of deriving registry keys or handler names from filenames.
Relevant files in the Next.js core:
packages/next/src/build/templates/edge-ssr.tspackages/next/src/build/templates/edge-app-route.tspackages/next/src/build/templates/pages-edge-api.ts- and
packages/next/src/build/templates/middleware.ts
Output Types
The outputs object contains arrays of build output types:
outputs.pages: React pages from thepages/directoryoutputs.pagesApi: API routes frompages/api/outputs.appPages: React pages from theapp/directoryoutputs.appRoutes: API and metadata routes fromapp/outputs.prerenders: ISR-enabled routes and static prerendersoutputs.staticFiles: Static assets and auto-statically optimized pagesoutputs.middleware: Middleware function (if present)
Note: When
config.outputis set to'export', onlyoutputs.staticFilesis populated. All other arrays (pages,appPages,pagesApi,appRoutes,prerenders) will be empty since the entire application is exported as static files.
For any route output with runtime: 'edge', edgeRuntime is included and contains the canonical entry metadata for invoking that output in your edge runtime.
Pages (outputs.pages)
React pages from the pages/ directory:
{
type: 'PAGES'
id: string // Route identifier
filePath: string // Path to the built file
pathname: string // URL pathname
sourcePage: string // Original source file path in pages/ directory
runtime: 'nodejs' | 'edge'
assets: Record<string, string> // Traced dependencies (key: relative path from repo root, value: absolute path)
wasmAssets?: Record<string, string> // Bundled wasm files (key: name, value: absolute path)
edgeRuntime?: {
modulePath: string // Absolute path to the module registered in the edge runtime
entryKey: string // Canonical key used by the edge entry registry
handlerExport: string // Export name to invoke, currently 'handler'
}
config: {
maxDuration?: number // Maximum duration of the route in seconds
preferredRegion?: string | string[] // Preferred deployment region
env?: Record<string, string> // Environment variables (edge runtime only)
}
}API Routes (outputs.pagesApi)
API routes from pages/api/:
{
type: 'PAGES_API'
id: string // Route identifier
filePath: string // Path to the built file
pathname: string // URL pathname
sourcePage: string // Original relative source file path
runtime: 'nodejs' | 'edge'
assets: Record<string, string> // Traced dependencies (key: relative path from repo root, value: absolute path)
wasmAssets?: Record<string, string> // Bundled wasm files (key: name, value: absolute path)
edgeRuntime?: {
modulePath: string // Absolute path to the module registered in the edge runtime
entryKey: string // Canonical key used by the edge entry registry
handlerExport: string // Export name to invoke, currently 'handler'
}
config: {
maxDuration?: number // Maximum duration of the route in seconds
preferredRegion?: string | string[] // Preferred deployment region
env?: Record<string, string> // Environment variables (edge runtime only)
}
}App Pages (outputs.appPages)
React pages from the app/ directory:
{
type: 'APP_PAGE'
id: string // Route identifier
filePath: string // Path to the built file
pathname: string // URL pathname.Includes .rsc suffix for RSC routes
sourcePage: string // Original relative source file path
runtime: 'nodejs' | 'edge' // Runtime the route is built for
assets: Record<string, string> // Traced dependencies (key: relative path from repo root, value: absolute path)
wasmAssets?: Record<string, string> // Bundled wasm files (key: name, value: absolute path)
edgeRuntime?: {
modulePath: string // Absolute path to the module registered in the edge runtime
entryKey: string // Canonical key used by the edge entry registry
handlerExport: string // Export name to invoke, currently 'handler'
}
config: {
maxDuration?: number // Maximum duration of the route in seconds
preferredRegion?: string | string[] // Preferred deployment region
env?: Record<string, string> // Environment variables (edge runtime only)
}
}App Routes (outputs.appRoutes)
API and metadata routes from the app/ directory:
{
type: 'APP_ROUTE'
id: string // Route identifier
filePath: string // Path to the built file
pathname: string // URL pathname
sourcePage: string // Original relative source file path
runtime: 'nodejs' | 'edge' // Runtime the route is built for
assets: Record<string, string> // Traced dependencies (key: relative path from repo root, value: absolute path)
wasmAssets?: Record<string, string> // Bundled wasm files (key: name, value: absolute path)
edgeRuntime?: {
modulePath: string // Absolute path to the module registered in the edge runtime
entryKey: string // Canonical key used by the edge entry registry
handlerExport: string // Export name to invoke, currently 'handler'
}
config: {
maxDuration?: number // Maximum duration of the route in seconds
preferredRegion?: string | string[] // Preferred deployment region
env?: Record<string, string> // Environment variables (edge runtime only)
}
}Prerenders (outputs.prerenders)
ISR-enabled routes and static prerenders:
{
type: 'PRERENDER'
id: string // Route identifier
pathname: string // URL pathname
parentOutputId: string // ID of the source page/route
groupId: number // Revalidation group identifier (prerenders with same groupId revalidate together)
pprChain?: {
headers: Record<string, string> // PPR chain headers (e.g., 'x-nextjs-resume': '1')
}
parentFallbackMode?: false | null | string // false: no additional paths (fallback: false), null: blocking render, string: path to HTML fallback
fallback?: {
filePath: string | undefined // Path to the fallback file (HTML, JSON, or RSC)
initialStatus?: number // Initial status code
initialHeaders?: Record<string, string | string[]> // Initial headers
initialExpiration?: number // Initial expiration time in seconds
initialRevalidate?: number | false // Initial revalidate time in seconds, or false for fully static
postponedState: string | undefined // Serialized PPR state used for resuming rendering
}
config: {
allowQuery?: string[] // Allowed query parameters considered for the cache key
allowHeader?: string[] // Allowed headers for ISR
bypassFor?: RouteHas[] // Cache bypass conditions
renderingMode?: 'STATIC' | 'PARTIALLY_STATIC' // STATIC: fully static, PARTIALLY_STATIC: PPR-enabled
partialFallback?: boolean // Serves a partial fallback shell that should be upgraded to a full route in the background
bypassToken?: string // Generated token that signals the prerender cache should be bypassed
}
}Static Files (outputs.staticFiles)
Static assets and auto-statically optimized pages:
{
type: 'STATIC_FILE'
id: string // Route identifier
filePath: string // Path to the built file
pathname: string // URL pathname
immutableHash: string | undefined // Content hash when the filename contains a hash, indicating the file is immutable
}Middleware (outputs.middleware)
middleware.ts (.js/.ts) or proxy.ts (.js/.ts) function (if present):
{
type: 'MIDDLEWARE'
id: string // Route identifier
filePath: string // Path to the built file
pathname: string // Always '/_middleware'
sourcePage: string // Always 'middleware'
runtime: 'nodejs' | 'edge' // Runtime the route is built for
assets: Record<string, string> // Traced dependencies (key: relative path from repo root, value: absolute path)
wasmAssets?: Record<string, string> // Bundled wasm files (key: name, value: absolute path)
edgeRuntime?: {
modulePath: string // Absolute path to the module registered in the edge runtime
entryKey: string // Canonical key used by the edge entry registry
handlerExport: string // Export name to invoke, currently 'handler'
}
config: {
maxDuration?: number // Maximum duration of the route in seconds
preferredRegion?: string | string[] // Preferred deployment region
env?: Record<string, string> // Environment variables (edge runtime only)
matchers?: Array<{
source: string // Source pattern
sourceRegex: string // Compiled regex for matching requests
has: RouteHas[] | undefined // Positive matching conditions
missing: RouteHas[] | undefined // Negative matching conditions
}>
}
}Routing Information
The routing object in onBuildComplete provides complete routing information with processed patterns ready for deployment:
routing.beforeMiddleware
Routes applied before middleware execution. These include generated header and redirect behavior.
routing.beforeFiles
Rewrite routes checked before filesystem route matching.
routing.afterFiles
Rewrite routes checked after filesystem route matching.
routing.dynamicRoutes
Dynamic matchers generated from route segments such as [slug] and catch-all routes.
routing.onMatch
Routes that apply after a successful match, such as immutable cache headers for hashed static assets.
routing.fallback
Final rewrite routes checked when earlier phases did not produce a match.
Common Route Fields
Each route entry can include:
source: Original route pattern (optional for generated internal rules)sourceRegex: Compiled regex for matching requestsdestination: Internal destination or redirect destinationheaders: Headers to applyhas: Positive matching conditionsmissing: Negative matching conditionsstatus: Redirect status codepriority: Internal route priority flag
Use Cases
Common use cases for adapters include:
- Deployment Platform Integration: Automatically configure build outputs for specific hosting platforms
- Asset Processing: Transform or optimize build outputs
- Monitoring Integration: Collect build metrics and route information
- Custom Bundling: Package outputs in platform-specific formats
- Build Validation: Ensure outputs meet specific requirements
- Route Generation: Use processed route information to generate platform-specific routing configs
Was this helpful?