Release candidate — 1.0.0-rc.1
StitchAPI

Elysia

An Elysia plugin for StitchAPI — a principal-bound seam on the request context, an SSE bridge with streamStitchSse returning a Response, and stitch errors mapped to HTTP via the plugin's onError.

Use @stitchapi/elysia when you serve an API with Elysia. .use() the plugin and every request gets a principal-bound seam on its context, plus two bridges into Elysia's Web-standard world: an SSE writer and a stitch-error → HTTP mapping.

Web-standard by construction. Elysia is Bun-first, but the plugin imports only elysia and stitchapi — there are no node:* imports, so it runs unchanged on Bun, Node, Deno, and the edge.

Example

Install the package alongside core and Elysia:

npm install @stitchapi/elysia stitchapi elysia

Build (and own) the seam once at startup, then .use() the plugin. With a principal resolver, each request's context stitch is a seam.as(id) handle — a principal-bound seam with separate auth sessions per principal over one shared throttle. A .derive runs per request and puts it on the context, so a handler reads it straight off the destructured context:

import { stitch } from '@stitchapi/elysia';
import { Elysia } from 'elysia';
import { seam } from 'stitchapi';
import { z } from 'zod';

const api = seam({ baseUrl: 'https://api.example.com' });

const app = new Elysia()
    .use(
        stitch({
            seam: api,
            principal: ({ request }) =>
                request.headers.get('x-tenant') ?? undefined,
        }),
    )
    .get('/me', ({ stitch }) =>
        stitch.stitch({
            path: '/me',
            output: z.object({ id: z.string(), name: z.string() }),
        })(),
    );

The principal lives in the closure, never in a call argument, so a handler can never name another identity. Returning undefined falls back to the unbound root seam for that request (e.g. anonymous). Borrow, don't own: the plugin never calls seam.close() — you build the seam at startup and await api.close() on shutdown.

Streaming — streamStitchSse

Stream a streaming/SSE stitch's .stream() to the client by returning the text/event-stream Response streamStitchSse builds. 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 cancels the body and aborts the upstream stitch stream.

import { stitch, streamStitchSse } from '@stitchapi/elysia';
import { Elysia } from 'elysia';
import { seam } from 'stitchapi';
import { sseSurface } from 'stitchapi/sse';

const api = seam({ baseUrl: 'https://api.example.com' });

const app = new Elysia()
    .use(stitch({ seam: api }))
    .get('/chat', ({ stitch, query }) => {
        const chat = stitch.stitch({ kind: sseSurface, path: '/v1/messages' });
        return streamStitchSse(chat.stream({ body: { prompt: query.q } }), {
            data: (chunk) => (chunk as { data: string }).data,
        });
    });

Anti-pattern: return the streamStitchSse Response as the handler's result — don't also set set.status / set.headers or return a second value from the same handler. The helper builds the complete text/event-stream response (status, headers, and the frame body); a competing write corrupts the stream.

Errors

A failed stitch throws a StitchError carrying the upstream status. The plugin registers an .onError that maps it to an HTTP response (502 by default, so an upstream's status is never leaked) and lets every other error fall through to Elysia's default handling — so handlers need no try/catch:

import { stitch } from '@stitchapi/elysia';
import { Elysia } from 'elysia';
import { seam } from 'stitchapi';

const api = seam({ baseUrl: 'https://api.example.com' });

const app = new Elysia().use(
    stitch({
        seam: api,
        // the default is a safe 502; propagate the upstream status instead:
        errorHandler: { status: (e) => e.status ?? 502 },
    }),
);

Set errorHandler: false to register none and wire your own with stitchOnError (an .onError-compatible mapper) or stitchErrorResponse(err, options) (a one-off StitchErrorResponse); isStitchError narrows an unknown error first.

On Bun clusters and other multi-runtime deployments, pair the seam with a shared store so throttle and sessions are fleet-wide rather than per-process.

See also

On this page