From install to first telemetry
Use Node.js 24 or newer. Express 4.18 and Express 5 are supported. Express is an optional peer dependency, so non-Express applications can still use the manual event API.
Available on npm: Install the public @inflowapm/node package with the command below.
npm install @inflowapm/node1. Create a project key
Registration, sign-in, session refresh, sign-out, and password recovery are connected to the current authentication API. After signing in, create a project and copy the api_key from the creation response. It is returned in raw form only when the project is created.
# You can create a project in the dashboard or through the API.
curl -X POST http://127.0.0.1:5002/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"dev@example.com","password":"replace-me","first_name":"Developer","last_name":"Example"}'
curl -X POST http://127.0.0.1:5002/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"dev@example.com","password":"replace-me"}'
curl -X POST http://127.0.0.1:5002/api/v1/projects \
-H "Authorization: Bearer USER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Checkout API"}'2. Configure the service
INFLOWAPM_API_KEY=iapm_project_key
INFLOWAPM_ENDPOINT=http://127.0.0.1:5002
INFLOWAPM_SERVICE=checkout-api
INFLOWAPM_ENVIRONMENT=development
INFLOWAPM_SERVICE_VERSION=1.4.03. Instrument Express
import express from "express";
import { InflowAPM } from "@inflowapm/node";
const app = express();
const inflow = new InflowAPM({
apiKey: process.env.INFLOWAPM_API_KEY,
endpoint: process.env.INFLOWAPM_ENDPOINT,
service: process.env.INFLOWAPM_SERVICE,
environment: process.env.INFLOWAPM_ENVIRONMENT,
serviceVersion: process.env.INFLOWAPM_SERVICE_VERSION ?? "1.4.0",
});
app.use(inflow.express());
app.get("/health", (_request, response) => response.send("ok"));
app.listen(3000);4. Run and verify
Start the application, send a request with a browser, curl, or Postman, then call await inflow.flush() while testing. Inspect the flush result or inflow.getStats(). The current analytics API is GET /api/v1/telemetry/analytics/dashboard?project_id=PROJECT_ID&range=24h and uses the user access token.
Configuration reference
Pass configuration explicitly at startup. The SDK validates it without throwing into application startup; invalid configuration disables telemetry and appears in getStats().configurationIssue.
| Option | Default | Purpose |
|---|---|---|
| apiKey | required | Project API key. Keep it on the server. |
| endpoint | required | HTTP(S) InflowAPM base URL; the SDK appends the ingestion path. |
| service | required | Stable service name, such as checkout-api. |
| environment | required | Deployment name, such as development, staging, or production. |
| serviceVersion | optional | Release version attached to telemetry. |
| enabled | true | Disable capture without removing instrumentation. |
| batchSize | 100 | Maximum events in one backend-compatible batch. |
| maxBufferSize | 1000 | Bounded in-memory event capacity. |
| flushThreshold | batch size | Buffered event count that schedules delivery. |
| flushIntervalMs | 5000 | Background delivery interval. |
| requestTimeoutMs | 5000 | Timeout for each ingestion request. |
| maxAttempts | 3 | Total delivery attempts for retryable failures. |
| shutdownTimeoutMs | 5000 | Bound for the final shutdown drain. |
| debug | false | Safe lifecycle diagnostics without payloads or keys. |
The endpoint must be an HTTP(S) base URL without credentials, query parameters, or a fragment. If it includes a path, the SDK preserves it and appends /api/v1/telemetry/ingest.
Express integration
Install inflow.express() before the routes you want to observe. The middleware records the matched route template after the response finishes, avoiding high-cardinality raw URLs.
const api = express.Router();
api.use(inflow.express({ routePrefix: "/api" }));
api.get("/orders/:orderId", getOrder);
app.use("/api", api);Mounted routers can provide routePrefix. Requests with no matched route are recorded as /__unmatched__. Supported methods are GET, POST, PUT, PATCH, DELETE, OPTIONS, and HEAD.
Automatic HTTP monitoring
The middleware records the normalized route, method, response status, duration, timestamp, service, environment, and optional service version. It also marks a response that closes before completion. It does not automatically capture thrown error objects or stack traces.
- Stable route templates
- Response status
- Request duration
- Premature close metadata
Manual events
Use captureEvent() for supported application work that is not represented by an HTTP request. Application event routes are currently query, error, and timeout.
const result = inflow.captureEvent({
type: "event",
route: "query",
durationMs: 87,
metadata: { operation: "load_checkout" },
});
if (!result.accepted) {
console.warn(result.reason);
}The return value reports whether the event was accepted into the local buffer. It does not claim that the backend has persisted the event.
Localhost and debugging
Use http://127.0.0.1:5002 when the application and InflowAPM backend run directly on the same machine. From a container, use a hostname reachable from that container instead of assuming its own localhost points to the host.
Browser / curl / Postman
send request
localhost:3000
your Express app
InflowAPM :5002
receives telemetry
Set debug: true only while diagnosing. Debug output covers lifecycle classifications and never logs the project key or event payload.
Staging, production, and self-hosting
Keep one stable service name across deployments and change the explicit environment value. Point endpoint at the reachable InflowAPM base URL for each environment. Environment filtering is not yet present in the frontend, so the configuration distinction is captured in telemetry rather than advertised as a dashboard control.
process.once("SIGTERM", async () => {
await inflow.shutdown();
server.close();
});The host application owns process signals. Call shutdown() from its existing shutdown path; the SDK does not install signal handlers or keep the process alive solely for telemetry.
Bounded, retrying, and fail-open
The middleware performs validation and a bounded in-memory append after the response lifecycle. Background delivery starts at the threshold, on the interval, after an explicit flush(), or during shutdown().
- Buffering
- The default buffer holds 1,000 events. New events are dropped when it is full; memory does not grow without a bound.
- Retry
- Network failures, timeouts, 408, 429, and 5xx responses retry with full-jitter backoff. Retry-After is respected.
- Permanent failures
- 400, 401, 403, 413, 422, and other non-retryable 4xx responses are classified without an endless retry loop.
- Fail-open
- Capture and delivery errors do not throw through the application request path.
- Rate limiting
- Backend 429 responses are counted and retried within the configured attempt limit; the SDK does not bypass server limits.
Keep credentials and payload data out
Server-side only. Never embed a project API key in browser, mobile, or other distributed client code. Treat it as a secret and rotate it if exposed.
Automatic Express instrumentation records route templates and timing data. It does not read the raw URL, query values, route parameter values, headers, authorization, cookies, or request and response bodies. It does not collect stack traces automatically. Manual identity fields and metadata are opt-in, so review them before sending sensitive or regulated data.
Request path and telemetry path
Request path
Client → Express middleware → route handler → application response
Telemetry path
Lifecycle callback → bounded buffer → retrying batch delivery → ingestion API → BullMQ → PostgreSQL analytics
The SDK is responsible through authenticated ingestion. The backend accepts batches, processes them asynchronously through BullMQ, stores project-scoped events in PostgreSQL, and exposes analytics through the current API.
Troubleshooting and current limits
No events are visible
Inspect getStats(), verify the project key and server-reachable endpoint, install middleware before routes, and call await inflow.flush() while testing.
Dynamic IDs appear in routes
Confirm Express matched a declared route and that the middleware can observe it. Use routePrefix for mounted routers. Raw unmatched paths intentionally become /__unmatched__.
The process exits before delivery
Await flush() in short-lived tasks or shutdown() in the host shutdown path. Unreferenced timers do not guarantee delivery after a serverless runtime freezes an invocation.
Current limitations
The SDK does not patch Express globally, capture stack traces automatically, persist its local buffer to disk, install process signal handlers, or provide browser/mobile instrumentation. Python support is not implemented yet.
For exact option ranges, typed return values, and package engineering notes, read the package README and source.
