TypeScript / Node
Batched log shipping plus an Express error handler that attaches the log lines leading to the failure.
Before you start
- Where your InsightRecorder runs
- The base URL every call goes to —
http://localhost:8080when you are running it locally, your own hostname otherwise. - An API key
- Settings → API keys, scoped to
logs:writeandbug:write. The token (crk_…) is shown exactly once — it is hashed at rest — and is revocable. Do not use a login JWT: it expires within the hour and is not meant for machines.
- 01
Install
Zero runtime dependencies; ships its own type declarations.
npm install @insightrecorder/insight-recorder - 02
Create the client and shipper
Once, at composition. Close it on shutdown or the last batch is lost.
import { Client } from "@insightrecorder/insight-recorder"; const client = new Client(process.env.INSIGHT_BASE_URL, process.env.INSIGHT_API_KEY); const shipper = client.shipper(); shipper.send({ level: "error", message: "checkout failed", traceId, attrs: { order_id: "1234" } }); await shipper.close(); - 03
Report unhandled errors
Register it LAST in the Express stack — an error handler before the routes never sees them.
app.use(errorHandler(client, { shipper, service: "checkout-api" })); - 04
Verify it is working
Ship one line and throw from a route. Register the error handler LAST, after your routes, or Express never reaches it.
shipper.send({ level: "error", message: "insightrecorder smoke test" }); app.get("/_boom", () => { throw new Error("insightrecorder smoke test"); });Then look for the line at /app/logs and the report at /app/bugs.
If nothing shows upNothing arrived? Check shipper.dropped — a rising count means the queue is undersized. Delivery errors are silent unless you pass onError. And await shipper.close() on shutdown, or the last batch is lost.
Before you ship
- — Each request is capped at the batch size, so a burst cannot exceed the server's body limit.
- — Retries are bounded and honour the server's 429 + Retry-After.
Paste this into a coding agent running inside your project, or point the agent at /docs/prompts/typescript directly. It is self-contained: the agent needs no prior knowledge of InsightRecorder.
# Install and wire the InsightRecorder TypeScript/JavaScript SDK
Paste this into a coding agent (Claude Code, Cursor, …) running inside your
Node.js service's repository. It is self-contained — the agent needs no prior
knowledge of InsightRecorder.
---
You are working in a Node.js service (TypeScript or JavaScript). Install and
wire the InsightRecorder SDK so this service ships its logs to InsightRecorder and reports
unhandled errors.
**Context you need:**
- InsightRecorder collects application logs and bug reports. `@insightrecorder/insight-recorder`
is the supported way for a Node **server** to talk to it. (A browser app uses
a different thing — a `<script>` capture agent — so do not install this
package in front-end-only code.)
- Requires **Node 18+**. The package has zero runtime dependencies.
- Authentication uses a long-lived **API key** shaped like `crk_…`, created in
the InsightRecorder UI under *Settings → API keys* with the `logs:write` scope (add
`bug:write` to report errors). **Do not** use a login JWT: those expire after
an hour and are not meant for machines.
Add `bug:read` for anything the SDK reads back (`listBugs/getBug`), and `logs:read` if you query logs. A write-only key is the common mistake: shipping works, the first read returns 403.
- **Wire trace correlation if the application uses OpenTelemetry.** A log line
and the request that wrote it are one investigation, and that join is the
reason to ship logs here rather than anywhere else — see the "Trace
correlation" section of the README for what this language needs (several do it
automatically; the rest take one hook). Without it, every log call has to carry
`trace_id` by hand, which nobody sustains past the first week.
**Steps:**
1. `npm install @insightrecorder/insight-recorder` (or the project's package manager —
match what the repo already uses: yarn, pnpm, bun).
2. Read configuration from the environment, never hard-code it. Add
`INSIGHT_BASE_URL` and `INSIGHT_API_KEY` to the project's existing config
loading and to `.env.example` / deployment manifests. If either is empty, the
service must start normally with shipping disabled — telemetry is never a
startup dependency.
3. In the service's composition root (where the logger and HTTP server are
built — do not invent a new bootstrap module if one exists):
```ts
import { Client, errorHandler } from "@insightrecorder/insight-recorder";
const client = new Client(process.env.INSIGHT_BASE_URL!, process.env.INSIGHT_API_KEY!);
const shipper = client.shipper();
```
4. Bridge the service's existing logger to `shipper.send(...)`. Match what the
repo already uses:
- **pino**: add a custom transport/stream, or call `shipper.send` from a
`hooks.logMethod` wrapper.
- **winston**: add a small custom `Transport` whose `log()` calls
`shipper.send`.
- **console only**: wrap the call sites, or leave logging alone and only wire
error reporting (step 5).
Map fields as: `level` → level, the message → `message`, a trace id →
`traceId`, and everything else into `attrs` (string values).
5. If the service uses Express, register the error handler **last**, after all
routes:
```ts
app.use(errorHandler(client, { shipper, service: "<service name>" }));
```
For Fastify/Koa/NestJS, call `client.reportBug(...)` from that framework's
own error hook instead — the payload shape is in the package's README.
6. Flush on shutdown. Add `await shipper.close()` to the service's existing
SIGTERM/SIGINT handling, next to the server close. Without it the final batch
is lost.
**Rules — do not violate these:**
- Do **not** write a custom fetch loop that POSTs one request per log line. The
SDK batches, retries with backoff, respects the server's quota (429 +
`Retry-After`), and caps each request at `batchSize`.
- Do **not** `await shipper.send(...)` or wrap it in anything that waits — it is
deliberately synchronous and non-blocking, and drops when its queue is full.
- Do **not** log the API key, and do not commit it.
- Do **not** capture request bodies, headers, cookies, or query strings in error
reports. The SDK records only the method and path, because those routinely
carry credentials and personal data.
**Verify before you finish:**
1. The project's build/typecheck and test commands pass.
2. Start the service with both env vars set, hit an endpoint, then confirm the
log lines appear in InsightRecorder at `/app/logs`.
3. Trigger a deliberate error and confirm a bug appears at `/app/bugs`.
4. Confirm the service still starts with both variables **unset**.
5. Report what you changed, and state explicitly whether `shipper.close()` runs
on shutdown.
If anything is ambiguous — which logger, where the composition root is, how
shutdown is handled — inspect the repository and follow what is already there.
Do not restructure the service to fit the SDK.