NestJS
Wire stitches into a NestJS app with StitchModule — injectable stitches, a Logger trace bridge, ConfigService-backed secrets, and request-scoped multi-tenancy.
Use @stitchapi/nest when you want your stitches available through NestJS
dependency injection — configured once, injected into services as typed
dependencies, with the framework's Logger and ConfigService wired in and
shutdown handled for you. It is a thin peer-dependency package that adds no
capability of its own: it wires StitchAPI's shared-runtime primitive (one store,
one trace sink, a trusted principal boundary) into Nest's DI graph, so core stays
untouched.
Example
Install the package alongside core:
npm install @stitchapi/nest@rc stitchapi@rcConfigure the shared client once in your root module. forRootAsync resolves its
options from an injected factory, which is how ConfigService reaches the base
URL, secrets, and store. The event stream is bridged to the Nest Logger by
default — nothing to switch on:
// app.module.ts
import { } from '@nestjs/common';
import { , } from '@nestjs/config';
import { , } from '@stitchapi/nest';
import { } from 'stitchapi/auth';
@({
: [
.({
: [],
: [],
: (: ) => ({
: .('API_BASE_URL'),
: (()('API_TOKEN')),
}),
}),
],
})
export class {}Declare a stitch with defineStitch, register it per feature with forFeature,
and inject it as a typed dependency:
// users.module.ts
import { , } from '@nestjs/common';
import {
,
type ,
,
,
} from '@stitchapi/nest';
import { } from 'zod';
export const = ('GET_USER', () =>
.({
: '/users/{id}',
: .({ : .(), : .() }),
}),
);
@({ : [.({ : [] })] })
export class {}
@()
export class {
constructor(
@()
private readonly : <typeof >,
) {}
(: string) {
return this.({ : { } });
}
}Injected<typeof GetUser> keeps the injection-site type tied to the definition,
so the injected getUser is the same typed callable a stitch always is — awaited
for the validated result, or .stream()-ed for the
event stream.
Call app.enableShutdownHooks() in main.ts. Without it Nest never fires
its shutdown hook, so the shared trace sink is not flushed and the store is
not closed on exit.
Options
forRoot({ store, trace, ...defaults }) configures the app-wide infrastructure —
one store, one trace sink — plus the shared config (baseUrl, auth, retry,
throttle, …) every stitch inherits; forRootAsync resolves the same options
from an injected factory. forFeature({ stitches, seam }) registers injectable
stitches and, optionally, a per-upstream seam built over the shared store and
trace — seam: { config, token? } (its config is the feature seam's
baseUrl/auth/…); omit seam to attach the stitches to the default one.
Two bridges connect the framework, and neither changes core:
loggerbridges a stitch's event stream into the NestLoggeras the seam'sTraceSink— on by default, matching the Fastify plugin. It logs only metadata (name, method, scrubbed URL, status, attempts, timing) — never bodies or headers. Disable it withlogger: false, or drop the happy path withlogger: { lifecycle: false }; an explicittracewins over the bridge.fromNestConfig(config)('KEY')is aConfigService-backed secret resolver — theConfigServicetwin ofenv(). It resolves synchronously at call time, so the credential never lands on the config or in a trace.
For multi-tenancy, bind a principal per request with seam.as(...). The principal
scopes sessions and cache over the one shared store, so each tenant gets its own
login while the connection pool stays shared:
import { } from '@nestjs/common';
import { } from '@nestjs/core';
import { } from '@stitchapi/nest';
import type { Seam } from 'stitchapi';
const = {
: 'TENANT_SEAM',
: .,
: (: Seam, : { ?: { ?: string } }) =>
.(.?. ?? 'anonymous'),
: [, ],
};Anti-pattern: don't swap the store per request to isolate tenants — bind
the principal with seam.as(tenantId) instead. Multi-tenancy is
principal-scoped over one shared store (per-tenant sessions and cache keys,
a single connection pool); a per-request store throws all of that away.
Outside an HTTP request — a queue worker or a scheduled job — there is no
request to read, so bind the principal explicitly from the job's own data.
See also
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.
Next.js
Web-standard helpers for Next App Router route handlers — streamStitchSse streams a stitch as text/event-stream, and stitchErrorResponse maps a StitchError to a Response.