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.
Use @stitchapi/fastify when you serve an API with Fastify
and want your stitches wired into its request lifecycle: one shared seam
decorated on the instance, a request-scoped principal bound per request, and
three bridges into Fastify's world — its
built-in Pino logger, SSE streaming, and a stitch-error → HTTP error handler. Like
@stitchapi/nest, it is a thin peer-dependency plugin that adds no capability of
its own: it wires StitchAPI's shared-runtime primitive (one store, one trace sink,
a trusted principal boundary) into Fastify, so core stays untouched.
Example
Install the package alongside core and Fastify:
npm install @stitchapi/fastify stitchapi fastifyRegister the plugin once. Give it a principal resolver and every request gets
its own seam.as(principal) handle on request.stitch — a separate session/token
over the shared store and throttle. The plugin is wrapped with
fastify-plugin, so the decorators escape its encapsulation and are visible
app-wide:
import { } from '@stitchapi/fastify';
import from 'fastify';
const = ({ : true });
.(, {
: { : 'https://api.example.com', : { : 3 } },
: () => .['x-tenant'] as string | undefined,
});
.('/me', async () => ..({ : '/me' })());request.stitch is the per-request principal handle; fastify.stitch is the root
seam, decorated app-wide. This is the trusted boundary StitchAPI's seam exists for:
the principal lives in the closure, so a handler can never name another identity.
Seam: build or borrow
Pass a seamConfig and the plugin builds and owns the seam — closing it on the
Fastify onClose hook. Pass a prebuilt seam instead and the plugin borrows
it: the app owns its lifecycle and the plugin never closes it.
import { } from '@stitchapi/fastify';
import from 'fastify';
import { } from 'stitchapi';
const = ({ : 'https://api.example.com' });
const = ();
// borrowed — the app owns `api`; override with `closeSeam: true` to force-close.
.(, { : });The ownership rule mirrors @stitchapi/nest: a seam the plugin built is closed for
you; a borrowed seam is yours to close.
Ambient principal — currentStitch()
currentStitch() reads the request's bound seam from Node's AsyncLocalStorage,
so services called from a handler need not thread request through every call — a
value-add a Node integration can offer that the browser-first core cannot. It
returns undefined outside a request, so a caller can fall back to an explicit
seam:
import { } from '@stitchapi/fastify';
// In a service called from a handler — no `request` threaded through:
async function () {
const = (); // the request's principal-bound seam
return ?.({ : '/profile' })();
}SSE streaming
sendStitchSse(reply, stream, options?) streams a stitch's .stream() output to a
text/event-stream reply. Each delta chunk becomes one SSE frame; an error
event ends the stream as a named error frame; stream end closes the response; and
a client disconnect aborts the upstream stitch generator rather than leaving it
running.
import { } from '@stitchapi/fastify';
import from 'fastify';
import { } from 'stitchapi/sse';
const = ({ : 'https://api.example.com/chat' });
const = ();
.<{ : { : string } }>('/chat', (, ) =>
(, .({ : { : .. } }), {
: () => ( as { : string })., // pull text out
}),
);Error handling
The plugin registers a setErrorHandler that maps a thrown StitchError to an
HTTP response (502 by default, so an upstream's status is never leaked) and
rethrows everything else, leaving Fastify's default handler in charge. Propagate
the upstream status instead by configuring it:
import { } from '@stitchapi/fastify';
import from 'fastify';
const = ();
.(, {
: { : 'https://api.example.com' },
: { : () => . ?? 502 },
});Set errorHandler: false to register none and wire your own with
stitchErrorHandler(options).
By default fastify.log (Fastify's built-in Pino logger) is bridged as the
seam's TraceSink, logging only metadata — name, method, scrubbed URL,
status, attempts, timing — never request/response bodies or headers, so it is
safe on a secret-bearing seam. Disable it with logger: false, or drop the
happy path with logger: { lifecycle: false }. See @stitchapi/pino
for the same sink as a standalone export.
See also
NestJS
Wire stitches into a NestJS app with StitchModule — injectable stitches, a Logger trace bridge, ConfigService-backed secrets, and request-scoped multi-tenancy.
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.