Node.js SDK

Express Middleware

Add a single error-handling middleware to your Express app to automatically capture all unhandled errors with stack traces, HTTP context, and correlation data.

Basic Setup

Register the Vigilry error middleware after all routes. Express identifies error middleware by its 4-argument signature (err, req, res, next).

app.ts
import express from "express";
import { Vigilry } from "@vigilry/node";

const app = express();
const vigilry = new Vigilry({ apiKey: process.env.VIGILRY_API_KEY! });

// ... your routes ...
app.get("/api/health", (req, res) => res.json({ ok: true }));

// Error middleware — MUST be last, after all routes
app.use(async (err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
  await vigilry.captureError(err, {
    status_code: 500,
    path: req.path,
    method: req.method,
    correlation: {
      // Attach any request-level context
      user_id: (req as any).user?.id,
    },
  });
  res.status(500).json({ error: "Internal server error" });
});

app.listen(3000);

Reusable Middleware Factory

Extract the middleware into a reusable factory so it can be shared across multiple Express apps in a monorepo.

middleware/vigilry-errors.ts
import { Vigilry } from "@vigilry/node";
import type { ErrorRequestHandler } from "express";

export function vigilryErrorHandler(vigilry: Vigilry): ErrorRequestHandler {
  return async (err, req, res, next) => {
    await vigilry.captureError(err, {
      status_code: (err as any).statusCode ?? 500,
      path: req.path,
      method: req.method,
      correlation: {
        user_id: (req as any).user?.id,
      },
    });
    next(err); // pass to your existing error handler
  };
}
app.ts
import express from "express";
import { Vigilry } from "@vigilry/node";
import { vigilryErrorHandler } from "./middleware/vigilry-errors";

const app = express();
const vigilry = new Vigilry({ apiKey: process.env.VIGILRY_API_KEY! });

app.use(express.json());
// ... routes ...

// Vigilry captures errors, then passes to your error handler
app.use(vigilryErrorHandler(vigilry));

// Your existing error handler
app.use((err: Error, req: express.Request, res: express.Response, _next: express.NextFunction) => {
  const status = (err as any).statusCode ?? 500;
  res.status(status).json({ error: err.message });
});

Capturing Business Events

Beyond errors, instrument key business flows directly in your route handlers.

routes/orders.ts
import { Router } from "express";
import { vigilry } from "../lib/vigilry";

const router = Router();

router.post("/orders", async (req, res) => {
  const order = await createOrder(req.body);

  // Track successful order creation
  await vigilry.capture({
    type: "order_created",
    severity: "info",
    message: `Order ${order.id} created`,
    correlation: {
      user_id: req.user.id,
      order_id: order.id,
      customer_id: req.user.customerId,
    },
  });

  res.status(201).json(order);
});

router.post("/orders/:id/cancel", async (req, res) => {
  const order = await cancelOrder(req.params.id);

  // Flag cancellations as a warning for risk analysis
  await vigilry.capture({
    type: "order_cancelled",
    severity: "warn",
    message: `Order ${order.id} cancelled by user`,
    correlation: {
      user_id: req.user.id,
      order_id: order.id,
    },
  });

  res.json(order);
});

export { router as ordersRouter };

Tips

Singleton pattern

Create one Vigilry instance (e.g. in lib/vigilry.ts) and import it everywhere. Creating multiple instances is safe but wastes connections.

Fire and forget

In high-throughput handlers you can skip await on capture() calls. The SDK queues the request internally and never blocks your response.

Correlation is key

Always include user_id and order_id (or equivalent) in correlation. This enables the risk worker to group related events and detect per-user anomalies.

Error middleware order

Vigilry error middleware should come before your final error handler so it can capture the error before a 500 is sent to the client.