Skip to contentOutRay/Docs
GitHub ↗
Get started ↗
Documentation

Your services, from local to live.

OutRay documentation

Products

ObservabilityConnect your first serviceFramework SDK setupServices and healthInvestigate requests and tracesLogs and metricsConfigure alerts

SDKs & integrations

Node.js SDKTanStack Start SDKNext.js PluginExpress PluginNestJS PluginHono SDK
Observability

Express Plugin

Register Express request telemetry and optionally open a development tunnel.

@outray/express supports an HTTP development tunnel and separately configured request telemetry. Observability can remain enabled when the tunnel is disabled.

Install

npm install @outray/express

The package supports Express 4 and 5. Its observability dependency requires Node.js 20 or later.

Development tunnel

Call the default export before app.listen():

import express from "express";
import outray from "@outray/express";

const app = express();

outray(app, {
  apiKey: process.env.OUTRAY_API_KEY,
  subdomain: "my-api",
});

app.get("/health", (_request, response) => {
  response.json({ ok: true });
});

app.listen(3000);

The tunnel starts only when NODE_ENV === "development" and enabled is true. The plugin detects the port when the HTTP server starts listening, including when you use an automatically assigned port.

Supply a token with tunnel:connect. The plugin does not reuse a saved outray login session.

Request telemetry

Register observability before body parsers and routes:

import express from "express";
import { registerOutrayObservability } from "@outray/express";

const app = express();
const telemetry = registerOutrayObservability(app, {
  apiKey: process.env.OBSERVABILITY_TOKEN,
  serviceName: "orders-api",
  environment: process.env.NODE_ENV,
  captureConsole: true,
  capturePayloads: {
    maxBodyBytes: 16 * 1024,
    redactedFields: ["accountPin"],
  },
});

app.use(express.json());

app.post("/orders", (request, response) => {
  telemetry.info("order received", { itemCount: request.body.items.length });
  response.json({ accepted: true });
});

app.listen(3000);

Use a server-only observability:write token. The returned object provides debug, info, warn, error, forceFlush(), and shutdown().

Request middleware records spans and HTTP request count, duration, and active-request metrics. It uses the matched route when available. request.routeResolver and request.ignore let you customize route naming and filtering.

This registration works without a tunnel or Node preloader. For automatic database, HTTP-client, or logger instrumentation, initialize the Node.js SDK before those libraries are imported. The SDK uses one active process-wide instance, so configure that bootstrap with the intended service and token.

Combining both

The default export also accepts an observability object:

outray(app, {
  apiKey: process.env.TUNNEL_TOKEN,
  observability: {
    apiKey: process.env.OBSERVABILITY_TOKEN,
    serviceName: "orders-api",
  },
});

The top-level apiKey configures the tunnel. The nested observability.apiKey configures telemetry. They can be different scoped tokens.

Tunnel options

OptionDefaultBehavior
apiKeyOUTRAY_API_KEYTunnel authentication
subdomainOUTRAY_SUBDOMAINRequested address
customDomainUnsetVerified custom tunnel hostname
serverUrlOUTRAY_SERVER_URL or wss://connect.outray.co/Tunnel server
enabledOUTRAY_ENABLED !== "false"Enable tunnel, still development-only
silentfalseSuppress tunnel status output
localfalseAdd mDNS LAN access

Lifecycle callbacks are onTunnelReady(url), onError(error), onClose(), onReconnecting(), and onLocalReady(info).

Payload capture and privacy

capturePayloads is opt-in and separate from tunnel enablement. Request bodies are captured only when a body parser exposes request.body; OutRay does not consume the request stream itself.

Eligible JSON and URL-encoded bodies are bounded and redacted. The defaults are 16 KiB per body and 8 KiB of serialized headers, with hard caps of 64 KiB and 32 KiB respectively. Add application-specific redactedFields and redactedHeaders before enabling capture.

Top-level capturePayloads without an observability object installs capture middleware but does not initialize an exporter; it requires an existing recording OpenTelemetry span.

This capture writes span attributes in Observability. It is not the same as full tunnel request capture in the Tunnels console. Review request and trace inspection before capturing real traffic.

Next.js Plugin

Use development tunnels and configure server-side Next.js telemetry independently.

NestJS Plugin

Configure NestJS telemetry before initialization and tunnels after listening.

On this page

InstallDevelopment tunnelRequest telemetryCombining bothTunnel optionsPayload capture and privacy