Back to docs

@nihal/flowlens-node

Backend span collection SDK for correlating server-side events with frontend traces. Supports Express, Fastify, and raw node:http.

Install

bash
npm install @nihal/flowlens-node

Express

Use flowlens() middleware for Express-style applications. CORS must allow the X-FlowLens-Trace-Id header.

typescript
import express from 'express'
import cors from 'cors'
import { flowlens } from '@nihal/flowlens-node'

const app = express()

app.use(cors({
  origin: true,
  allowedHeaders: ['Content-Type', 'X-FlowLens-Trace-Id']
}))
app.use(flowlens({
  serviceName: 'my-api'
}))

app.get('/api/users', (req, res) => {
  res.json({ users: [] })
})

app.listen(3000)

Fastify

Use flowlensFastify() plugin for Fastify servers.

typescript
import Fastify from 'fastify'
import { flowlensFastify } from '@nihal/flowlens-node'

const app = Fastify()

app.register(flowlensFastify({
  serviceName: 'my-api'
}))

app.get('/api/users', async () => {
  return { users: [] }
})

app.listen({ port: 3000 })

Raw HTTP

Use wrapHandler() for raw node:http servers.

typescript
import { createServer } from 'node:http'
import { wrapHandler } from '@nihal/flowlens-node'

const handler = (req, res) => {
  res.writeHead(200)
  res.end('OK')
}

const server = createServer(
  wrapHandler(handler, { serviceName: 'my-api' })
)

server.listen(3000)

Config

typescript
interface FlowLensNodeConfig {
  serviceName: string       // required
  collectorUrl?: string     // default: "http://localhost:9229"
  enabled?: boolean         // default: true
  headerName?: string       // default: "x-flowlens-trace-id"
}
OptionDefaultDescription
serviceNamerequiredName identifying your service in traces
collectorUrl"http://localhost:9229"FlowLens span collector endpoint
enabledtrueEnable/disable span reporting
headerName"x-flowlens-trace-id"Header name for trace ID propagation

How It Works

FlowLens auto-injects a X-FlowLens-Trace-Id header into all outgoing fetch/XHR requests from the embedded page. When a request hits your backend:

  1. 1. Reads the X-FlowLens-Trace-Id header.
  2. 2. If no trace ID, passes through with zero overhead.
  3. 3. Captures stacks at: request (ingress), handler (execution), response (egress).
  4. 4. Sends the span payload fire-and-forget POST to the collector on :9229.

Span Payload

typescript
// Fields sent to the collector
{
  traceId: string
  route: string
  method: string
  statusCode: number
  duration: number
  serviceName: string
  timestamp: number
  requestStack: string
  handlerStack: string
  responseStack: string
}

The collector splits each span into three backend-span events with phases: request, handler, response.