— 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
npm install @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)));
});
// server.ts
import Fastify from "fastify";
import {
  emitVisionFastifyPlugin,
  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 = Fastify();
await app.register(emitVisionFastifyPlugin); // captures errors in onError, flushes in onResponse

// ... your routes ...

await app.listen({ port: 3001 });
process.on("SIGTERM", () => {
  void flush()
    .catch((err) => console.error("emit-vision flush failed", err))
    .finally(() => app.close().then(() => 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. Always await 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.