Reference

Adapter behavior

Compare routing, parsing, streaming, context, and lifecycle ownership across every supported host adapter.

All adapters use the shared route executor for contract input/output validation, context and middleware. Native routers retain host routing semantics.

Custom adapters can provide AdapterRouteInput.readHeader(name) for synchronous reads of individual normalized header names. Body media-type checks use it when no explicit headers record or parsed-body contentType takes precedence. Existing adapters can continue providing only readHeaders(). A declared header schema still needs the full record from headers or readHeaders(); both readers must describe the same request headers. The Fetch adapter materializes and caches that record per request only when it is needed.

Hono, H3 and Elysia use native single-header reads for body checks. Node HTTP, Express, Fastify and Koa share nodeRequestHeader and nodeRequestHeaders from @hulla/api-node, preserving repeated values and own header names while excluding HTTP/2 pseudo-headers. Express and Fastify cache full header records per request when a header schema requires them.

BoundaryRouting / HEADBody ownership and limitsCancellation
Fetch and Fetch-based framework wrappersCore static-path precedence, then method; explicit HEAD declarations; 404/405 from coreOwned reader: no library-imposed default cap; optional preservationNative Request signal; returned streams close their producer on cancel
Node HTTPCore routing; HEAD executes GET and suppresses the bodyOwned reader: opt-in byte limit, actual chunk countDisconnect aborts portable signal and closes stream/reader
ExpressExpress routing and HEAD fallbackConfigure Express parser limits; parsed body reusedDisconnect aborts portable signal; shared Node writer
FastifyFastify routing and HEAD configurationConfigure host bodyLimit; parsed body reusedDisconnect aborts the portable signal through the shared Node lifetime helper
H3H3 routing and HEAD behaviorOwned reader: no library-imposed default cap; optional preservationNative Request signal and shared Fetch stream writer
HonoHono routing and HEAD behaviorOwned reader: opt-in byte limit; earlier host parsers retain host limitsNative Request signal and shared Fetch stream writer
ElysiaElysia routing and HEAD behaviorOwned reader: opt-in byte limit; earlier parsed bodies retain host limitsNative Request signal and shared Fetch stream writer
In-processContract route selection and core executorApplication values; no byte serialization or byte limitCaller signal; returned iterable ownership
MessagePortContract path/method through coreStructured-clone wire protocol; configure bridge-level message limitsRequest cancel and endpoint closure propagate to pending work/streams
AWS Lambda / Azure FunctionsCore dispatch after host extractionHost already buffered the request; configure platform limitsHost-dependent; no client-disconnect guarantee

A static route for one method can shadow a parameter route for another method under core routing. Do not rely on identical overlap resolution in every native router; use unambiguous route layouts for portable deployment. Native mounts should be tested through their actual router, not only a mocked reply object.

Adapter-owned body readers impose no byte cap by default. Set maxBodyBytes explicitly on Fetch, Node HTTP, H3, Hono or Elysia to opt into a limit. Existing host/parser/platform limits still apply. Conformance verifies explicitly configured limits rather than requiring a shared default across hosts.

Query input uses absent fields, scalar singleton fields and arrays for repeated fields across Fetch and in-process execution. In-process normalization copies only when needed and never round-trips through URL or JSON text. Binary, FormData and structured application values remain transport-owned; raw native Response bodies are not universally portable.

The shared Fetch and Node response writers notify onError for native serialization errors and late stream failures. A response can be replaced before commitment. After commitment a hook is observational and cannot change the already-sent status. Framework-owned serialization outside those writers follows that host’s error policy.

Cancellation is cooperative. Passing signal makes downstream work cancellable; it cannot terminate arbitrary user promises. Consumers must exhaust streams or explicitly return/cancel them. The package closes transport-owned streams when decoding fails, when a socket disconnects, and when an IPC endpoint closes.

Executable conformance

scripts/adapter-conformance.ts registers the same contract and assertions for each host. Supply open(implementation) returning a client transport, a cleanup function, and an optional HTTP request function. Each test gets a fresh implementation, context counter, middleware trace, and host. Cleanup runs after each test, including failures. scripts/conformance-http.ts provides real ephemeral TCP servers for Node-based hosts so response backpressure and disconnects reach their native writers.

The shared cases cover encoded path parameters, singleton/repeated query fields, per-request context, middleware order, JSON/text/bytes/empty responses, multipart repeated fields and file bytes, separate Set-Cookie values, declared errors in both client modes, malformed JSON and recovery, cooperative cancellation, full stream consumption, early return, and late producer failure. Keep native context, routing, body-limit and platform-specific lifecycle tests alongside this suite; passing the portable laws does not prove every host feature.

Execution pathShared suite entryExplicit host difference
Fetchtransport-conformance.test.tsUses native Request/Response without a network socket
In-processtransport-conformance.test.tsNo malformed JSON text scenario: input is already an application value
MessagePorttransport-conformance.test.tsNo malformed JSON text scenario: messages use structured clone
Node HTTPconformance.test.tsReal TCP server
Expressconformance.test.tsReal TCP server; multipart parser is application-owned
Fastifyconformance.test.tsReal TCP server; multipart requires a host plugin
Honoconformance.test.tsNative router and Fetch handler
H3conformance.test.tsSafe native parameter decoding preserves %2F instead of producing /
Elysiaconformance.test.tsNative router and Fetch handler

A capability must be true or a concrete explanation. Unsupported scenarios appear as named exclusions in test output; do not silently drop assertions or catch failures to make an integration pass. The encoded-slash exception asserts H3’s specific returned value. Any new exception must also be explained in this matrix and the host’s documentation.

Run a native adapter’s shared suite with bun x vitest run packages/<adapter>/tests/conformance.test.ts, or the three transport suites with bun x vitest run packages/message-port/tests/transport-conformance.test.ts. Build core first when running directly from a clean checkout. Normal package tests run these files automatically, and Turbo tracks changes to both shared helpers.

Remaining host boundaries and evidence

The following packages have their own host tests; they do not yet register the shared suite above. This distinction is intentional evidence tracking, not an assertion that every platform shares identical transport semantics.

IntegrationRepresentations and streaming boundaryRouting / host ownershipExisting host evidence
Cloudflare Workers / PagesFetch representations and Web streamsWorker fetch / Pages file routing; native bindings and execution contextTests
AWS LambdaPayload v2 base64/text input; response streams are collected into a buffered proxy resultHTTP API v2 / Function URL event; platform owns request limits and invocation lifetimeTests
Azure FunctionsNative HTTP request/result mapping; byte streams passed as async iterablesFunctions v4 HTTP trigger; host owns buffering and lifetimeTests
Google Cloud functionsBuffered rawBody input when provided; shared Node streaming response writerFunctions Framework request/response context and platform request limitsTests
Netlify FunctionsFetch representations and Web streamsWeb-native synchronous Functions context and platform deploymentTests
Next.jsFetch representationsApp Router; rejects QUERY; request context and Data Cache belong to NextProduction fixture
TanStack StartFetch representationsWildcard server routes; rejects QUERY; loader and hydration lifecycle belong to StartProduction fixture
React RouterFetch representationsResource routes; rejects QUERY; HEAD dispatches GET and suppresses its bodyProduction fixture
SolidStartFetch representationsAPI routes; rejects QUERY; native event and GET/HEAD mappingProduction fixture
SvelteKitFetch endpoints and colocated remote-function transportRejects QUERY; native RequestEvent and remote-function lifecycleProduction fixture
NuxtNitro HTTP and request-aware client transportRejects QUERY; native H3 event, HEAD mapping and SSR request stateProduction fixture
AstroFetch endpoints and explicit-context in-process callsNative APIContext, HEAD mapping and server-island lifecycleProduction fixture

For Fetch-derived wrappers, the shared executor does not independently verify the wrapper’s host-context extraction, method mapping, credentials, deployment configuration, or rendering boundary. Those remain host-fixture responsibilities. Raw native response objects are transport-specific; a buffered platform response must not be advertised as live streaming.

Published consumers and compatibility

bun run check:packages checks every publishable package after bun run build. It creates actual package tarballs and installs each package in its own temporary directory outside the repository, together with packed core, its declared peers, and a consumer Node type environment. It audits imports in emitted JavaScript and declarations against the package’s own dependency declarations, resolves published declaration imports, emits consumer declarations, imports the built entrypoints under Node, and executes a core client/server roundtrip. Temporary consumers are removed even after failure.

Express, Node HTTP, MessagePort, WebSocket, NestJS, Next, Nuxt, Astro and Netlify also have dedicated packed consumer fixtures. These exercise adapter mounting/native context or transport execution with the same installed core instance. Express additionally checks cookies, HEAD and malformed JSON. The SvelteKit remote entrypoint’s $app/server runtime import is verified by its framework production fixture; its published declaration surface is still checked by the isolated consumer. External library declaration internals use skipLibCheck; our emitted import resolution and consumer declaration emission are checked explicitly.

scripts/check-packages.ts owns the host-major profiles and emits the CI matrix with --matrix. Adding a multi-major peer range without profiles for every advertised major fails the package check.

CheckVersions selectedEvidence produced
Baseline packed packagesLocally installed peer versions from the workspace lockfileAll published entrypoints and dedicated fixtures listed above
ExpressExact Express 4.18.0 / types 4.17.25; latest matching Express 5 / types 5Real TCP roundtrip, native context, cookies, HEAD, malformed JSON, declarations
NextLatest matching major 15 and 16Native NextRequest, mounted route handler and declarations
NuxtLatest matching major 3 and 4Installed Nuxt peer, H3 handler execution and declarations
AstroLatest matching major 5, 6 and 7Native APIRoute type compatibility, minimal APIContext endpoint and colocated transport
NetlifyLatest matching Functions major 4 and 5Native Context type compatibility and handler execution
Portable Node runtime22.18.0, 24.11.0 and latest 26.xCore and MessagePort laws plus native adapter tests, including shared conformance

These host-major smoke checks complement production fixtures at the locked development version. They do not claim to build a complete application on every historical minor version or simulate cloud deployment. Profiles log the actual resolved peer versions so failures can be reproduced. Run one with bun run check:compatibility --profile=express-4, or one baseline package with bun run check:packages --package=node-http. CI uses Node 24 for host-major consumers; the portable runtime has its own Node matrix. A failing compatibility assertion must be fixed or the advertised peer range narrowed with documented evidence.

Adapter and transport completion checklist

Before calling a new integration complete:

  • Public boundary: export a stable package/subpath, use the shared runtime, preserve typed native context, and verify complete implementations and fragments. Ensure the public declaration can be emitted by a consumer.
  • Portable laws: register the shared suite through the real host/router. Record every unsupported capability and parameter-decoding difference with a specific reason and an assertion where applicable.
  • Representation ownership: verify JSON, text, bytes, multipart fields/files, empty results, response headers/cookies, streams and raw responses where supported. Document who parses the body, enforces byte limits and owns unread bodies.
  • Routing: test base paths, encoded parameters, static/parameter overlap, unsupported methods, 404/405, HEAD body suppression and host middleware/fallback behavior. Preserve and document native semantics.
  • Lifetime and errors: test malformed input, declared and unexpected errors, cooperative cancellation, disconnects, early stream return, late producer failure, cleanup, and error-hook behavior before/after response commitment.
  • Native context: exercise request/event extraction, middleware state, per-request isolation and incompatible-adapter rejection through host-level tests; portable context tests alone are insufficient.
  • Production consumption: provide a runnable production fixture where the framework has a build/rendering boundary. Verify browser/server separation, request-scoped authentication/context and SSR/hydration behavior where relevant.
  • Published package: pass check:packages, including import ownership, extracted tarball resolution and consumer declarations. Add a dedicated packed consumer when the adapter introduces a new native or runtime boundary.
  • Compatibility: declare supported runtimes/host versions and add relevant CI profiles. Keep major-version smoke evidence separate from production-build and platform-deployment evidence.
  • Documentation: add the package to this matrix and document setup, representations, limits, routing, cancellation, native context, unsupported features, and the commands that reproduce its verification.

The same checklist applies when extending an existing adapter with a new transport or runtime mode. Keep application-owned features such as authentication policy, retry policy and cache invalidation in application/framework composition.

Additional native integrations

  • WebSocket runs the shared suite over real sockets, including multipart wire values, pull-driven streams and cancellation. Malformed HTTP JSON is inapplicable; dedicated tests reject malformed wire messages and cover disconnect cleanup.
  • NestJS runs the shared suite on Express and Fastify. Multipart parsing remains host/plugin-owned and is explicitly excluded. Native tests cover request-scoped DI, guards, interceptors, metadata, prefixes and fragments. Packed consumers exercise both platform adapters against NestJS 11 and 12.
  • Bun and Deno fixtures use real HTTP servers. The Vercel fixture checks the built Web Handler locally; it does not certify a cloud deployment. All three cover byte bodies above 1 MiB, cookies, streaming and routing. Run bun run check:runtimes, or npm exec --yes --package=deno@2.9.6 -- bun run check:runtimes --deno.

See WebSocket, NestJS and runtime hosts for ownership and setup.

Koa and desktop bridges

Koa 3 uses the shared suite over real TCP plus native tests for typed state, upstream middleware, parsed-body reuse, limits, HEAD and 404/405 behavior. Its terminal middleware uses core routing and leaves response commitment to Koa. See Koa for ownership and commands.

Desktop string relays run the MessagePort laws through JSON encoding. Tauri and Dioxus lifecycle tests simulate the native channel boundary; they are not packaged native-app tests. Electron reuses the existing EventTarget/EventEmitter MessagePort implementation. See desktop bridges for native setup and lifecycle responsibilities.