did-btcr2-js

ADR 032: Sans-I/O handleRequest / handleSse Primitives

Status: Accepted

Date: 2026-04-22

Branch / PR: aggregation/http-transport Depends on: ADR 028

Context

HttpServerTransport has to integrate with many HTTP framework choices that a service operator might prefer: Hono, Express, Fastify, Bun’s native server, Cloudflare Workers, Deno Deploy, AWS Lambda, etc. Each has different primitives for:

The codebase also has a standing constraint: all code must be browser-compatible; no Node.js-only APIs. This precludes bundling node:http as an internal dependency even for the server side: server deployments might run in Workers or Deno.

Existing precedent in the repo for I/O-agnostic patterns:

Options considered

  1. Bundle node:http internally. Node-only; violates browser-compat constraint and locks out Workers/Deno deployments.
  2. Framework-specific adapters. Ship @did-btcr2/method/transport/http/express, @did-btcr2/method/transport/http/hono, etc. Multiple adapters to maintain, and new frameworks need us to add support.
  3. Expose a single fetch-style handler. The server is a (Request) => Promise<Response> function. Idiomatic in Workers / Deno; awkward for Express.
  4. Expose sans-I/O handleRequest / handleSse primitives over framework-agnostic HttpRequestLike / HttpResponseLike / SseStream types.

Decision

Option 4. HttpServerTransport exposes two methods:

handleRequest(req: HttpRequestLike): Promise<HttpResponseLike>;
handleSse(req: HttpRequestLike, stream: SseStream): void;

The caller writes ~15–30 lines of framework-specific glue to convert their framework’s request/response/stream primitives into and out of these shapes. The transport has zero direct I/O dependencies.

HttpRequestLike is a plain structural type: { method, url, headers, body?, remoteAddr? }. HttpResponseLike is { status, headers, body }. SseStream has writeEvent, writeComment, close, onClose.

Consequences

Positive

Negative

Explicitly accepted trade-offs

References