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/expressThe 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
| Option | Default | Behavior |
|---|---|---|
apiKey | OUTRAY_API_KEY | Tunnel authentication |
subdomain | OUTRAY_SUBDOMAIN | Requested address |
customDomain | Unset | Verified custom tunnel hostname |
serverUrl | OUTRAY_SERVER_URL or wss://connect.outray.co/ | Tunnel server |
enabled | OUTRAY_ENABLED !== "false" | Enable tunnel, still development-only |
silent | false | Suppress tunnel status output |
local | false | Add 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.