Hono
Edge-ready Hono middleware for StitchAPI — a principal-bound seam on c.var.stitch, an SSE bridge with streamStitchSse, and stitch errors mapped to HTTPException.
Use @stitchapi/hono when you serve an API with Hono on the
edge or across runtimes. It is three thin bridges between StitchAPI's backend
primitive (the seam) and Hono's Fetch-based request model: middleware that puts
a principal-bound seam on the request context, an SSE helper, and a stitch-error →
HTTP mapper.
Edge/multi-runtime by construction. Every import is from hono or
stitchapi; there are no node:* imports, so the package runs
unchanged on Node, Cloudflare Workers, Deno, Bun, and Vercel Edge.
Example
Install the package alongside core and Hono:
npm install @stitchapi/hono stitchapi honoBuild (and own) the seam once at startup, then register the middleware. With a
principal resolver, each request's c.var.stitch is a seam.as(id) handle — a
principal-bound seam with separate auth sessions per principal over one shared
throttle. Parametrise your app with StitchEnv so c.get('stitch') is typed:
import { type StitchEnv, } from '@stitchapi/hono';
import { } from 'hono';
import { } from 'stitchapi';
import { } from 'zod';
const = ({ : 'https://api.example.com' });
const = new <StitchEnv>();
// Put a principal-bound seam on every request's context.
.(({ : , : () => ..('x-tenant') }));
.('/me', async () => {
const = await .('stitch').({
: '/me',
: .({ : .(), : .() }),
})();
return .();
});The principal lives in the closure, never in a call argument, so a handler can
never name another identity. Borrow, don't own: the middleware never calls
seam.close() — you build the seam at startup and close it on shutdown.
Streaming — streamStitchSse
Stream a streaming/SSE stitch's .stream() to the client as Server-Sent Events,
via Hono's streamSSE. Each delta becomes a data: message; a terminal error
(or a throw) becomes a final event: error message; control events are consumed
but not forwarded; and a client disconnect aborts the upstream stitch stream.
import { type StitchEnv, , } from '@stitchapi/hono';
import { } from 'hono';
import { } from 'stitchapi';
import { } from 'stitchapi/sse';
const = ({ : 'https://api.example.com' });
const = new <StitchEnv>();
.(({ : }));
.('/chat', () => {
const =
.('stitch')
.({ : , : '/v1/messages' });
return (
,
.({ : { : ..('q') } }),
{ : () => ( as { : string }). },
);
});Errors — stitchOnError
A failed stitch rejects with a StitchError carrying the upstream status.
Register stitchOnError() as your app's onError so handlers need no per-route
try/catch — it maps a StitchError to a Hono HTTPException (502 by default,
so an upstream's status is never leaked) and rethrows everything else:
import { } from '@stitchapi/hono';
import { } from 'hono';
const = new ();
// default 502 — an upstream's 401/404/etc. is never leaked to your client.
.(());
// or propagate the upstream status instead:
.(({ : () => . ?? 502 }));Or map a single error by hand with stitchError(err), which returns an
HTTPException — or undefined for a non-Stitch error, so you can rethrow it
untouched.
On Cloudflare Workers and other multi-runtime deployments, pair the seam
with a shared store (e.g.
@stitchapi/redis) so throttle and sessions are fleet-wide rather than
per-isolate.
See also
Fastify
Register stitchPlugin and your Fastify app gets a shared seam, a request-scoped principal, and bridges into Fastify's Pino logger, SSE streaming, and error handling.
React
Tearing-free useStitch and useStitchStream hooks built on useSyncExternalStore, over the framework-agnostic @stitchapi/query-core store — plus an optional TanStack Query adapter.