# API reference: Runtime adapters

The runtime adapters wrap the same `app.fetch(Request)` dispatch for each platform. They are available *only* as subpaths (never from the root barrel), so runtime-specific code such as `node:http` never leaks into an edge or Worker bundle. For guide-level setup per platform, see the [adapters overview](/docs/adapters); for the module map, see the [API reference overview](/docs/api-reference).

**Diagram: One app, every runtime**

- **app.fetch(request)** (source, web-standard core) - Request -> Response
- **serve(app)** (servers) - /node · /bun · /deno
- **toFetchHandler(app)** (edge & serverless) - /cloudflare · /vercel · /fastly
- **toLambdaHandler(app)** (aws) - /lambda (APIGW v1 + v2)
- **Response** (converge, same behavior) - identical headers, errors, and docs everywhere

Adapters translate each platform's entrypoint into the same web-standard dispatch, so behavior stays identical across runtimes.

## `@daloyjs/core/node`

```ts
serve(app: App, opts?: NodeServerOptions): NodeServerHandle;

interface NodeServerOptions {
  port?:                 number;   // default: 3000
  hostname?:             string;   // default: "0.0.0.0"
  connectionTimeoutMs?:  number;   // default: 30_000
  shutdownTimeoutMs?:    number;   // default: 10_000
  handleSignals?:        boolean;  // default: true (SIGINT/SIGTERM)
  maxHeaderBytes?:       number;   // default: 16 KiB
  trustProxy?:           boolean;  // honor x-forwarded-proto/host (only behind a trusted LB)
  maxConnections?:       number;   // cap concurrent sockets (admission control); default: unset (unbounded)
  bufferedBodyMaxBytes?: number;   // default: 256 KiB (pre-buffer threshold for POST hot path)
}
interface NodeServerHandle { server: Server; port: number; close(): Promise<void> }
```

Pass `port: 0` when a test needs an ephemeral port. Because Node binds asynchronously, wait for the server's `listening` event before reading `handle.port`; it then reports the OS-assigned port instead of `0`.

```ts
import { once } from "node:events";

const handle = serve(app, { port: 0, handleSignals: false });
await once(handle.server, "listening");

const baseUrl = `http://127.0.0.1:${handle.port}`;
```

## `@daloyjs/core/bun`

```ts
serve(app: App, opts?: BunServeOptions): BunServerHandle;

interface BunServeOptions {
  port?:               number;
  hostname?:           string;
  maxRequestBodySize?: number;  // default: 16 MiB
  idleTimeout?:        number;
  development?:        boolean;
  unix?:               string;
  tls?:                BunTLSOptions;
}
interface BunServerHandle { port: number; url: URL | undefined; stop(): Promise<void> }
```

## `@daloyjs/core/deno`

```ts
serve(app: App, opts?: DenoServeOptions): DenoServerHandle;

interface DenoServeOptions {
  port?: number; hostname?: string;
  signal?: AbortSignal;
  cert?: string; key?: string;                   // HTTPS pair
  onListen?: (info: { hostname: string; port: number }) => void;
  onError?:  (err: unknown) => Response | Promise<Response>;
  handleSignals?: boolean;                       // default: true
  shutdownTimeoutMs?: number;                    // default: 10_000
}
interface DenoServerHandle { shutdown(): Promise<void> }
```

## `@daloyjs/core/cloudflare`

```ts
toFetchHandler<Env = unknown>(app: App): ExportedFetchHandler<Env>;
  // export default toFetchHandler(app);

interface ExportedFetchHandler<Env = unknown> {
  fetch: (request: Request, env?: Env, ctx?: { waitUntil?; passThroughOnException? }) => Promise<Response>;
}
```

## `@daloyjs/core/vercel`

```ts
type WebHandler = (req: Request) => Promise<Response>;
interface FetchHandler { fetch: WebHandler }
type RouteHandlers = Record<"GET"|"POST"|"PUT"|"PATCH"|"DELETE"|"OPTIONS"|"HEAD", WebHandler>;

toWebHandler   (app: App): WebHandler;        // bare function (middleware, deprecated Edge runtime)
toFetchHandler (app: App): FetchHandler;      // default export for Node Functions
toRouteHandlers(app: App): RouteHandlers;     // Next.js App Router route.ts
```

## `@daloyjs/core/fastly`

```ts
toFastlyHandler(app: App): (req: Request) => Promise<Response>;
installFastlyListener(app: App): void;   // wires addEventListener("fetch", ...)
```

## `@daloyjs/core/lambda`

```ts
toLambdaHandler(app: App): LambdaHandler;
toLambdaStreamHandler(app: App): LambdaStreamHandler;

type LambdaHandler  = (event: LambdaEvent) => Promise<LambdaResponse>;
type LambdaStreamHandler = (event: LambdaEvent, responseStream: LambdaResponseStream, context?: unknown) => Promise<void>;
type LambdaEvent    = LambdaEventV1   | LambdaEventV2;     // API Gateway REST + HTTP/Function URLs
type LambdaResponse = LambdaResponseV1 | LambdaResponseV2;
```

## Test-only / internal helpers

These are exported for internal tests and tooling. They are public-typed but underscore-prefixed; they may change without a semver bump. Most application code will never need them.

```ts
_resetCrashHandlersForTests();
_resetInsecureDefaultsLogForTests();
_resetCompressionRuntimeProbeForTests();
_resetSharedRateLimitStoresForTests();
```

---

Source: https://daloyjs.dev/docs/api-reference/adapters