Skip to content

Frameworks and runtimes

Next.js, NestJS and Express, queues and scripts, Vercel functions and AWS Lambda.

On this page

The rule is the same everywhere: the SDK has to be loaded before any provider client is constructed, because clients capture fetch when they are created. What changes is who owns startup.

Any Node server

Express, Fastify, Hono, Koa, a plain http.Server — add the preload flag to the command you already run.

Keep your existing entry file and argumentsTerminal
node --import @tracehatch/sdk/auto server.js

Every inbound request handled by http.Server or https.Server that makes a model call becomes a run named METHOD /path, marked failed on a 5xx status.

Next.js

Next.js owns startup, so it loads the SDK from instrumentation.ts instead.

instrumentation.ts · beside app/ or pages/, inside src/ when you use itTypeScript
export async function register() {
  if (process.env.NEXT_RUNTIME === "nodejs") {
    await import("@tracehatch/sdk/auto")
  }
}
  • Save it beside your app or pages directory — inside src when your app uses src/app. Merge into an existing register() if you have one.
  • Keep the key in .env.local.
  • Use the Node.js runtime for AI routes. The Edge runtime and browser code are not supported by the Node SDK.
  • Route handlers finish before export does, so hand the last flush to after():
app/api/chat/route.tsTypeScript
import { after } from "next/server"
import { flush } from "@tracehatch/sdk"

export async function POST(request: Request) {
  const response = await answer(request)
  after(() => flush())
  return response
}

NestJS

src/main.ts — the first line, before every other importTypeScript
import "@tracehatch/sdk/auto"

import { NestFactory } from "@nestjs/core"
import { AppModule } from "./app.module"

async function bootstrap() {
  const app = await NestFactory.create(AppModule)
  await app.listen(3000)
}
void bootstrap()

This works for standard compiled NestJS entrypoints in CommonJS and NodeNext. Custom entrypoints, alternate CLI configurations and Nest monorepo layouts need the same import placed by hand — the installer reports these rather than guessing at them.

Flush during graceful shutdown if you enable Nest's shutdown hooks; otherwise the SDK's own exit handling covers it.

Scripts, queues and workers

Outside an inbound request, a model call starts a run. If the response asks for tools, the run stays open for the tool step and the follow-up call with the results; it ends on a response without tool calls, a failure, a new user message, ten idle minutes, or process exit.

Name the unit of work yourself when "one model call" is not the unit you care about:

worker.tsTypeScript
import { trace, flush } from "@tracehatch/sdk"

for await (const job of queue) {
  await trace(`process ${job.type}`, async (run) => {
    run.setMetadata({ jobId: job.id })
    return handle(job)
  })
}
await flush()

Vercel functions

Nothing extra: the final flush is handed to waitUntil. Configure TRACEHATCH_API_KEY in the project's server environment variables, never a NEXT_PUBLIC_ one.

AWS Lambda, Netlify and similar

The process can be frozen the instant the handler returns, so wait for export:

handler.tsTypeScript
import "@tracehatch/sdk/auto"
import { flush } from "@tracehatch/sdk"

export async function handler(event) {
  try {
    return await answer(event)
  } finally {
    await flush()
  }
}

Load the SDK through the layer's preload option (NODE_OPTIONS) when the handler file is not the first module to run.

TypeScript runners

tsx accepts Node CLI flags as a documented drop-in replacement, so tsx --import @tracehatch/sdk/auto server.ts works. ts-node and ts-node-dev are recognised by the installer when it edits your scripts. A script already using --require is reported rather than rewritten.

Not supported

RuntimeUse instead
BrowsersNothing. Never ship the key or the SDK to a client.
Edge runtimes and WorkersProvider gateway
DenoProvider gateway
Python and other languagesGateway or log endpoint

Bun runs the package but its compatibility is unverified.

Frameworks on top of a provider client

Agent frameworks that call a supported API shape through the global fetch are captured with no extra work, because capture sits below them at the HTTP boundary. A framework that ships its own HTTP stack, or that is handed a custom fetch, is not — record those calls with a manual generation span.