— Node.js SDK
Node.js SDK (sdk-node)
Server-side instrumentation for Express, Fastify, and background workers. Uses the same ingest format as the browser SDK, so all events land in the same dashboard.
Install
pnpm add @emit-vision/sdk-node
Initialization
Call init() once at process startup, before any routes or workers run. Immediately after, call installProcessHandlers() to capture uncaught exceptions and unhandled promise rejections at the process level.
import { init, installProcessHandlers } from "@emit-vision/sdk-node";
init({
apiKey: process.env.EMIT_VISION_API_KEY,
environment: process.env.NODE_ENV,
release: process.env.APP_RELEASE,
});
installProcessHandlers();installProcessHandlers() covers unexpected process-level failures that middleware alone cannot see — always call it right after init().
HTTP frameworks
Choose the integration that matches your server framework. Both capture route errors and flush telemetry automatically on each response.
// server.ts
import express from "express";
import {
emitVisionExpress,
emitVisionRequestContext,
flush,
init,
installProcessHandlers,
} from "@emit-vision/sdk-node";
init({
apiKey: process.env.EMIT_VISION_API_KEY,
environment: process.env.NODE_ENV,
release: process.env.APP_RELEASE,
});
installProcessHandlers();
const app = express();
app.use(emitVisionRequestContext()); // scope URL, method, user-agent to each request
// ... your routes ...
app.use(emitVisionExpress()); // error handler — must come after all routes
const server = app.listen(3000);
process.on("SIGTERM", () => {
void flush()
.catch((err) => console.error("emit-vision flush failed", err))
.finally(() => server.close(() => process.exit(0)));
});Background workers and cron jobs
For processes with no HTTP request context, call the SDK directly and flush in the finally block so telemetry is delivered before the worker exits.
import {
captureError,
captureEvent,
flush,
init,
installProcessHandlers,
} from "@emit-vision/sdk-node";
init({
apiKey: process.env.EMIT_VISION_API_KEY,
environment: process.env.NODE_ENV,
release: process.env.APP_RELEASE,
});
installProcessHandlers();
async function runJob() {
captureEvent("worker_started", { queue: "email-delivery" });
try {
await deliverEmailBatch();
captureEvent("email_batch_delivered", { queue: "email-delivery" });
} catch (error) {
captureError(
error instanceof Error ? error : new Error("job failed"),
{ context: { queue: "email-delivery" } },
);
} finally {
await flush(); // always flush before the job ends
}
}
process.on("SIGTERM", () => void flush().finally(() => process.exit(0)));
void runJob();Logging vs. telemetry
Keep these two concerns separate:
- SDK — product events, handled exceptions, user identification, and structured context you want to query later.
- Logger — operational details like debug lines, retries, and infrastructure diagnostics.
- Avoid sending secrets, auth headers, passwords, or raw request bodies through either channel unless you have explicitly scrubbed them first.
Common mistakes
- Calling
init()more than once — a second call resets the internal queue. Call it once at the top of your entry point. - Forgetting
installProcessHandlers()— middleware only sees errors that reach the HTTP layer. Process crashes bypass it entirely. - Not flushing before
process.exit()— batched events are lost when the process exits. Alwaysawait flush()in your SIGTERM handler. - Sending raw request bodies — request bodies often contain PII or credentials. Never attach them to event properties or error context without explicit scrubbing.