@koderlabs/tasks-sdk-nestjs
v0.1.3
Published
NestJS module for InstantTasks — reporter + interceptor + breadcrumbs + req context + http/console/spans/typeorm tracing.
Maintainers
Readme
@koderlabs/tasks-sdk-nestjs
Runtime: Node.js with NestJS 9+. Server-only. Does not use the core
@koderlabs/tasks-sdkbrowser client — uses a server-side management key + directfetchto the InstantTasks ingest API.
Production-grade observability for NestJS apps: errors, request context, breadcrumbs, spans, HTTP tracing, console capture, TypeORM tracing.
Install
npm install @koderlabs/tasks-sdk-nestjs
# or
pnpm add @koderlabs/tasks-sdk-nestjs
# or
yarn add @koderlabs/tasks-sdk-nestjsPeer dependencies: @nestjs/common, @nestjs/core. typeorm is an optional
peer required only when captureTypeOrm: true.
Quick start
import { InstantTasksModule, InstantTasksReportInterceptor } from '@koderlabs/tasks-sdk-nestjs';
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
imports: [
InstantTasksModule.forRoot({
apiUrl: process.env.INSTANTTASKS_API_URL!,
projectId: process.env.INSTANTTASKS_PROJECT_ID!,
managementKey: process.env.INSTANTTASKS_MANAGEMENT_KEY!,
environment: process.env.NODE_ENV,
release: process.env.GIT_SHA,
// Opt-in observability — all default false
captureRequestContext: true,
captureHttp: true,
captureConsole: true,
captureSpans: true,
captureTypeOrm: true,
dataSource,
}),
],
providers: [
{ provide: APP_INTERCEPTOR, useClass: InstantTasksReportInterceptor },
],
})
export class AppModule {}Options
| Option | Type | Default | Purpose |
|---|---|---|---|
| apiUrl | string | — | InstantTasks base URL (no trailing slash; /api/v1 stripped) |
| projectId | string | — | Project UUID |
| managementKey | string | — | sk_* server key |
| environment | string | process.env.NODE_ENV | Free-form environment tag |
| release | string | 'prod' | Release identifier (git sha) |
| beforeSend | (e) => e \| null | — | Last chance to mutate/drop |
| dryRun | boolean | false | Skip network — useful in tests |
| debug | boolean | false | Verbose logs |
| captureRequestContext | boolean | false | Install ALS middleware capturing method/route/url/ip/userAgent/userId per request |
| captureHttp | boolean | false | Patch fetch + http.request + https.request, emit http breadcrumbs |
| captureConsole | boolean | false | Patch console.log/warn/error, emit console breadcrumbs (output preserved; Nest Logger messages skipped) |
| captureSpans | boolean | false | Wrap every controller call in a http.<method>.<route> span attached to the request context |
| captureTypeOrm | boolean | false | Install a TypeORM Logger that emits db breadcrumbs + db.query spans |
| dataSource | DataSource | — | Required when captureTypeOrm: true |
| ignoreUrls | Array<string \| RegExp> | [] | URLs not to record as HTTP breadcrumbs (own ingest is added automatically) |
| maxBreadcrumbs | number | 50 | Per-request ring capacity |
How it composes
HTTP request
↓
RequestContextMiddleware → starts AsyncLocalStorage scope with empty ring
↓
SpanInterceptor → records http.<method>.<route> span on success/error
↓
your controller
├─ fetch() → http breadcrumb (sanitized URL, status, durationMs)
├─ console.log() → console breadcrumb (output still goes to stdout)
└─ TypeORM query → db breadcrumb + db.query span
↓
ReportInterceptor (5xx) → Reporter.reportError() reads ALS, attaches:
{ request, breadcrumbs, spans } → POST /api/v1/sdk/eventsOut-of-band errors (uncaughtException, unhandledrejection) still report,
but without request context — they fire outside any HTTP scope.
Manual breadcrumbs
import { leaveBreadcrumb } from '@koderlabs/tasks-sdk-nestjs';
@Injectable()
export class MyService {
doThing() {
leaveBreadcrumb({ category: 'custom', message: 'started thing', data: { id: 42 } });
}
}leaveBreadcrumb() is a no-op outside an HTTP request scope.
Gotchas
AsyncLocalStorage doesn't follow detached callbacks
Code that escapes the request scope loses the ring:
- Bull / BullMQ processors run on a separate event loop tick — wrap your
handler in
requestContextStorage.run(ring, ...)if you need breadcrumbs. setTimeoutscheduled inside a request DOES inherit the scope.- Custom EventEmitters that fire after the request response is sent still see the scope (memory-leak risk if you hold the ring forever).
Console capture skips Nest Logger output
We pattern-match [Nest] / NestApplication / RoutesResolver in the
first argument and skip those messages. This prevents feedback loops when
AllExceptionsFilter logs via Nest Logger which eventually hits
console.error.
TypeORM Logger is exclusive
TypeORM allows exactly one logger per DataSource. captureTypeOrm: true
replaces whatever logger the host configured. If you have a custom logger,
wrap it instead:
import { InstantTasksTypeOrmLogger } from '@koderlabs/tasks-sdk-nestjs';
class CombinedLogger extends InstantTasksTypeOrmLogger {
override logQuery(sql, params) {
super.logQuery(sql, params);
yourExistingLogger.logQuery(sql, params);
}
}
dataSource.setOptions({ logger: new CombinedLogger() });HTTP tracing patches globals
Patches globalThis.fetch and require('http')/https.request.
Idempotent via a Symbol sentinel — calling installHttpTracing() twice is
safe. Restores on onModuleDestroy.
Sanitization mirrors @koderlabs/tasks-sdk-web-network:
Authorization,Cookie,Set-Cookie,X-Auth-Token,Proxy-Authorizationheaders →[Filtered]?token=…,?access_token=…,?api_key=…,?secret=…query values →[Filtered]
The SDK's own ingest endpoint (/api/v1/sdk/) is added to ignoreUrls
automatically to avoid recursion.
Span interceptor only runs in HTTP context
Skipped for WebSocket, GraphQL, microservice transports. If you need spans
for those, wire your own interceptor and call getCurrentContext()?.addSpan().
Tests
pnpm --filter @koderlabs/tasks-sdk-nestjs test19 tests cover: breadcrumb ring isolation, request-context middleware, fetch tracing, console capture, span interceptor, end-to-end integration with all flags wired.
Default-off principle
Every new capability defaults to false. With no flags set, behaviour is
identical to the MVP — only uncaughtException, unhandledRejection, and
5xx HTTP responses get reported. Enable features incrementally.
Suite overview
Full SDK suite map + platform availability matrix: docs/sdk/overview.md.
License
KoderLabs proprietary. See LICENSE for terms. Use of this package requires a separate signed written agreement with KoderLabs; access alone confers no rights.
Licensing inquiries: [email protected]
