NextAdapter routing context structure
The routing object in onBuildComplete context contains the following properties: beforeMiddleware (routes executed before middleware, includes header and redirect handling), beforeFiles (rewrite routes checked before filesystem route matching), afterFiles (rewrite routes checked after filesystem route matching), dynamicRoutes (dynamic route matching table), onMatch (routes applied after a successful match, for example immutable static asset cache headers), fallback (final rewrite fallback routes), shouldNormalizeNextData (whether /_next/data/<buildId>/... URLs should be normalized during matching), and rsc (route metadata used for React Server Components routing behavior).
NextAdapter.modifyConfig method signature and parameters
The modifyConfig method is async and takes two parameters: config (the complete Next.js configuration object) and context. The context object contains: phase (the current build phase as defined in phases reference), nextVersion (version of Next.js being used), and projectDir (absolute path to the Next.js project directory). The method returns the modified configuration object and can be async.
NextAdapter.onBuildComplete method signature and parameters
The onBuildComplete method is async and takes a context parameter called after the build process completes. The context object contains: routing (object with routing phase metadata including beforeMiddleware, beforeFiles, afterFiles, dynamicRoutes, onMatch, fallback, shouldNormalizeNextData, and rsc), outputs (detailed information about all build outputs organized by type), projectDir (absolute path to the Next.js project directory), repoRoot (absolute path to the detected repository root), distDir (absolute path to the build output directory), config (the final Next.js configuration with modifyConfig applied), nextVersion (version of Next.js being used), and buildId (unique identifier for the current build).
resolveRoutes return object properties
The resolveRoutes function returns an object with the following properties: middlewareResponded (boolean, true when middleware already sent a response and the adapter should not invoke an entrypoint), externalRewrite (a URL when routing resolved to an external rewrite destination), redirect (an object with url (URL) and status properties when the request should be redirected), 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), resolvedHeaders (a Headers object containing any headers added or modified during routing), status (an HTTP status code set by routing, for example from a redirect or rewrite rule), and routeMatches (a record of named matches extracted from dynamic route segments).
resolvedPathname vs invocationTarget distinction
For dynamic routes, resolvedPathname is the matched route template (such as /blog/[slug]), while invocationTarget.pathname is the concrete pathname to invoke. 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.
resolveRoutes usage example
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: new URL(requestUrl),
buildId,
basePath: config.basePath || '',
i18n: config.i18n,
headers: new Headers(requestHeaders),
requestBody,
pathnames,
routes: routing,
invokeMiddleware: async (ctx) => {
return {}
},
})
if (result.resolvedPathname) {
console.log('Resolved pathname:', result.resolvedPathname)
console.log('Resolved query:', result.resolvedQuery)
console.log('Invocation target:', result.invocationTarget)
}
@next/routing package
The @next/routing package is used to reproduce Next.js route matching behavior with data from onBuildComplete in adapters. It can be imported from npm at https://www.npmjs.com/package/@next/routing.
resolveRoutes function signature and parameters
The resolveRoutes function accepts an object with the following properties: url (a URL object for the request URL), buildId, basePath (from config, defaults to empty string if not provided), i18n (from config), headers (a Headers object with request headers), requestBody (a ReadableStream), pathnames (array of all pathname strings from outputs), routes (routing configuration), and invokeMiddleware (an async function that receives a context object and invokes platform-specific middleware, returning an object).
NEXT_TEST_DEPLOY_LOGS_SCRIPT_PATH environment variable
NEXT_TEST_DEPLOY_LOGS_SCRIPT_PATH is an environment variable that specifies the path to the executable that returns build and runtime logs for the deployment. The test harness looks for this variable.
NEXT_TEST_CLEANUP_SCRIPT_PATH environment variable
NEXT_TEST_CLEANUP_SCRIPT_PATH is an environment variable that specifies the path to an optional executable that tears down the deployment after the test run. The test harness looks for this variable.
Cleanup script purpose
The cleanup script can be used to clean up any resources created by the deploy script. It runs after the tests have completed.
Cleanup script environment variables
The cleanup script receives NEXT_TEST_DIR and NEXT_TEST_DEPLOY_URL as environment variables in addition to being executed with cwd set to the isolated temporary app.
Logs script required output markers
The logs script output must include lines starting with BUILD_ID:, DEPLOYMENT_ID:, and NEXT_SUPPORTS_IMMUTABLE_ASSETS:. After those markers, the logs script can print any additional build or server logs.
Deploy script contract stdout output
The deploy script must print the deployment URL to stdout, which will be used to verify the deployment. Avoid writing anything else to stdout.
Deploy script data persistence
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.
Logs script environment variables
The logs script receives NEXT_TEST_DIR and NEXT_TEST_DEPLOY_URL as environment variables in addition to being executed with cwd set to the isolated temporary app.
Testing Adapters overview
Next.js provides a test harness for validating adapters. Running end-to-end tests for deployment involves using custom lifecycle scripts and environment variables to build, deploy, and clean up test apps.
NEXT_TEST_DEPLOY_SCRIPT_PATH environment variable
NEXT_TEST_DEPLOY_SCRIPT_PATH is an environment variable that specifies the path to the executable that builds and deploys the isolated test app. The test harness looks for this variable.
Deploy script contract exit code
The deploy script must exit with a non-zero code on failure to signal deployment failure to the test harness.
Deploy script working directory
The deploy script is executed with cwd set to the isolated temporary app created by the Next.js test harness.
Deploy script contract diagnostic output
The deploy script must write diagnostic output to stderr or to files inside the working directory.
Example GitHub Actions workflow for adapter testing
The GitHub Actions workflow demonstrates how to set up adapter testing with Next.js using the test harness. It includes a build job that checks out the adapter and Next.js repositories, installs dependencies, builds the adapter, and caches artifacts. The test job runs in parallel across 16 groups, retrieving cached artifacts, setting environment variables for the adapter scripts, and running node run-tests.js with appropriate parameters including matrix group distribution and test type e2e.
Logs script pattern with file replay
One 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. Each platform has different ways to get the logs.
Example logs script
#!/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"
fi
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"
# Build the app
pnpm build
# 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 enables immutable static assets, set this to "1".
NEXT_SUPPORTS_IMMUTABLE_ASSETS="0"
{
echo "BUILD_ID: $BUILD_ID"
echo "DEPLOYMENT_ID: $DEPLOYMENT_ID"
echo "NEXT_SUPPORTS_IMMUTABLE_ASSETS: $NEXT_SUPPORTS_IMMUTABLE_ASSETS"
} >> .adapter-build.log
# 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"
PPR runtime flow concatenates shell and resumed render streams
At request time for PPR routes, the adapter serves a single concatenated response containing: (1) cached HTML shell stream, then (2) resumed render stream generated by invoking the handler with postponed state. The client receives one HTTP response with shell bytes followed by resumed bytes, allowing the shell to be sent immediately while rendering completes in the background.
Handler invocation with postponed state for PPR
When invoking the handler for PPR routes, pass postponed state via requestMeta.postponed property. The handler uses this postponed state to resume rendering and generate the resumed chunks that get appended to the response stream.
requestMeta.onCacheEntry is deprecated
The requestMeta.onCacheEntry callback still works but is deprecated. Adapters should use requestMeta.onCacheEntryV2 instead. If an adapter uses an internal onCacheCallback abstraction, it should wire it to requestMeta.onCacheEntryV2.
requestMeta.onCacheEntryV2 persists updated PPR cache entries
Use requestMeta.onCacheEntryV2 to persist updated shell and postponed data when a response cache entry is looked up or generated. Extract html using toUnchunkedString() method if available, then store shell, postponedState, headers, status, and cacheControl in platform cache.
requestMeta.onCacheEntryV2 callback signature
The requestMeta.onCacheEntryV2 callback is called with two parameters: cacheEntry and meta. The meta object contains url property. The callback receives cache entries with kind property (e.g., 'APP_PAGE'). For APP_PAGE entries, cacheEntry.value contains html, postponed, headers, and status properties. The callback must return false to continue normal Next.js response flow or true if the adapter already handled the response.
Seed PPR entries at build time with fallback and postponedState
At build time, adapters should seed PPR entries by reading the fallback HTML file and storing it along with the postponedState and initial metadata (initialHeaders, initialStatus, initialRevalidate, initialExpiration) in platform cache keyed by prerender.pathname.
onBuildComplete provides PPR fallback data
For partially prerendered app routes, the onBuildComplete hook provides outputs.prerenders array entries with fallback data. Each prerender has outputs.prerenders[].fallback.filePath (path to generated fallback shell HTML) and outputs.prerenders[].fallback.postponedState (serialized postponed state used to resume rendering).
Fallback data properties in onBuildComplete
Each prerender.fallback object from onBuildComplete contains: filePath (string path to generated fallback shell), postponedState (serialized state for resuming render), initialHeaders (object), initialStatus (number), initialRevalidate (number or false), and initialExpiration (number).
Adapter is build-time, cache interfaces are runtime
The Deployment Adapter API is a build-time interface that tells your platform what was built and how to route requests. Runtime behavior including request handling, streaming, and caching is handled by the Next.js server itself and by the cache interfaces cacheHandler and cacheHandlers.
cacheHandler vs cacheHandlers distinction
cacheHandler manages ISR and server cache storage and revalidation across instances. cacheHandlers configures 'use cache' directive backends and tag coordination.
ctx.waitUntil function in adapter handler context
The ctx.waitUntil function accepts a promise and keeps the serverless function alive after the response is sent, allowing background work like cache revalidation to complete.
requestMeta.onCacheEntryV2 callback behavior
The requestMeta.onCacheEntryV2 callback (set via addRequestMeta) fires when a cache entry is generated or looked up. It allows observing all cache operations and propagating cache updates to the platform's storage backend. This callback fires on the instance that handled the request. For multi-instance deployments, the adapter should propagate updates to shared storage.
PPR resume protocol POST request with postponedState
When an adapter detects a PPR-enabled route with a cached static shell, it should set the pprChain.headers on the internal request to the Next.js handler, send the request as a POST with the postponedState as the request body, and the handler will render only the deferred Suspense boundaries and stream the result.
pprChain.headers contains resume protocol header
In the prerenders output type, pprChain.headers contains the headers needed for the resume protocol. Specifically, it contains { 'next-resume': '1' }.
Node.js requestMeta fields
When invoking Node.js entrypoints, adapters can pass requestMeta with the following supported fields: relativeProjectDir (string, relative path from process.cwd() to the Next.js project directory), hostname (optional string, used by route handlers when constructing absolute URLs), revalidate (optional async function accepting {urlPath, headers, opts} for platform-specific revalidation), and render404 (optional async function accepting (req, res, parsedUrl, setHeaders) for rendering the 404 page for pages router notFound: true).
Node.js runtime handler interface
Node.js entrypoints use a handler(req, res, ctx) interface. The req parameter is an IncomingMessage, res is a ServerResponse, and ctx is an object with optional waitUntil (a function accepting Promise<void>) and optional requestMeta (an object with runtime-specific metadata).
Invoking edge runtime handler
After loading and evaluating chunks for modulePath in the edge runtime, use entryKey to read the registered entry from the global edge entry registry at globalThis._ENTRIES, then invoke the handlerExport from that entry. Use edgeRuntime metadata instead of deriving registry keys or handler names from filenames.
Edge runtime metadata object
For outputs with runtime: 'edge', Next.js provides output.edgeRuntime with metadata needed to invoke the entrypoint. It contains: modulePath (string, absolute path to the module registered in the edge runtime), entryKey (string, canonical key used by the edge entry registry), and handlerExport (string, export name to invoke, currently always 'handler').
Edge runtime handler interface (deprecated)
Edge entrypoints use a handler(request, ctx) interface where request is a Request object and ctx is an object with optional waitUntil (function accepting Promise<void>), optional signal (AbortSignal), and optional requestMeta. The handler returns Promise<Response>. The Edge Runtime is deprecated; new routes should use the Node.js runtime.
Adapters use case: Monitoring Integration
Monitoring Integration is a use case for adapters that collects build metrics and route information.
Adapters use case: Custom Bundling
Custom Bundling is a use case for adapters that packages outputs in platform-specific formats.
Adapters use case: Deployment Platform Integration
Deployment Platform Integration is a use case for adapters that automatically configures build outputs for specific hosting platforms.
Adapters use case: Asset Processing
Asset Processing is a use case for adapters that transforms or optimizes build outputs.
Adapters use case: Route Generation
Route Generation is a use case for adapters that uses processed route information to generate platform-specific routing configs.
Adapters use case: Build Validation
Build Validation is a use case for adapters that ensures outputs meet specific requirements.
routing.onMatch
routing.onMatch contains routes that apply after a successful match, such as immutable cache headers for hashed static assets.
routing.fallback
routing.fallback contains final rewrite routes checked when earlier phases did not produce a match.
Route entry fields in routing object
Each route entry in the routing object can include the following fields: source (original route pattern, optional for generated internal rules), sourceRegex (compiled regex for matching requests), destination (internal destination or redirect destination), headers (headers to apply), has (positive matching conditions), missing (negative matching conditions), status (redirect status code), and priority (internal route priority flag).
routing object in onBuildComplete
The routing object in onBuildComplete provides complete routing information with processed patterns ready for deployment.
routing.beforeMiddleware
routing.beforeMiddleware contains routes applied before middleware execution. These include generated header and redirect behavior.
routing.beforeFiles
routing.beforeFiles contains rewrite routes checked before filesystem route matching.
routing.afterFiles
routing.afterFiles contains rewrite routes checked after filesystem route matching.
routing.dynamicRoutes
routing.dynamicRoutes contains dynamic matchers generated from route segments such as [slug] and catch-all routes.
Adapters purpose
Adapters are used to build and validate deployment adapters that integrate with the Next.js build and runtime model.
Adapters documentation sections
The Next.js adapters API reference is organized into multiple sections: Configuration, Creating an Adapter, API Reference, Testing Adapters, Routing with @next/routing, Runtime Integration, Invoking Entrypoints, Output Types, Routing Information, and Use Cases. In the App Router, there are additional sections for Implementing PPR in an Adapter and Supporting Immutable Static Assets.