npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

auto-api-observe

v1.3.2

Published

Zero-config observability for Node.js APIs — Express, Fastify, Koa, Hono, NestJS, Next.js, Hapi, Elysia, Apollo, Lambda, tRPC, Restify. Request tracing, DB profiling, slow-request detection, and real-time metrics.

Readme


The Problem

You ship a Node.js API. Then you need to know: which routes are slow? What's your error rate? How many DB queries per request? Which third-party API is adding latency?

Datadog costs $23/host/month. New Relic wants your credit card. Grafana takes an afternoon to configure.

The Solution

app.use(require('auto-api-observe')({ apiKey: 'sk_live_...' }));

One line. Every request is tracked with latency, trace IDs, DB profiling, outbound HTTP calls, process metrics, and sensitive field masking — shipped to your dashboard at apilens.rest.


Framework Support

| Framework | Import | Style | |-----------|--------|-------| | Express | require('auto-api-observe') | app.use(observe(...)) | | Fastify | { fastifyObservability } | fastify.register(...) | | Koa | { koaObservability } | app.use(...) | | Hono | { honoObservability } | app.use(...) | | NestJS | { createNestObservabilityInterceptor } | Global interceptor | | Next.js | { withObservability } | API route wrapper | | Hapi | { hapiObservabilityPlugin } | server.register(...) | | Elysia | { elysiaObservability } | Plugin | | Apollo Server | { apolloObservabilityPlugin } | Plugin | | AWS Lambda | { withLambdaObservability } | Handler wrapper | | tRPC | { createTrpcObservabilityMiddleware } | t.middleware() | | Restify | { createRestifyMiddleware } | server.use(...) |


Install

npm install auto-api-observe

No extra dependencies. Pure Node.js.


Quick Start

Express

const express = require('express');
const observe = require('auto-api-observe');

const app = express();
app.use(observe({ apiKey: process.env.APILENS_KEY }));

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

Fastify

const fastify = require('fastify')();
const { fastifyObservability } = require('auto-api-observe');

await fastify.register(fastifyObservability, { apiKey: process.env.APILENS_KEY });

fastify.get('/users', async () => ({ users: [] }));
await fastify.listen({ port: 3000 });

Koa

const Koa = require('koa');
const { koaObservability } = require('auto-api-observe');

const app = new Koa();
app.use(koaObservability({ apiKey: process.env.APILENS_KEY }));

Hono

import { Hono } from 'hono';
import { honoObservability } from 'auto-api-observe';

const app = new Hono();
app.use('*', honoObservability({ apiKey: process.env.APILENS_KEY }));

NestJS

// main.ts
import { createNestObservabilityInterceptor } from 'auto-api-observe';

const Interceptor = createNestObservabilityInterceptor({ apiKey: process.env.APILENS_KEY });
app.useGlobalInterceptors(new Interceptor());

Next.js (API Routes)

import { withObservability } from 'auto-api-observe';
import type { NextApiRequest, NextApiResponse } from 'next';

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  res.json({ ok: true });
};

export default withObservability(handler, { apiKey: process.env.APILENS_KEY });

Hapi

const { hapiObservabilityPlugin } = require('auto-api-observe');

await server.register({ plugin: hapiObservabilityPlugin, options: { apiKey: process.env.APILENS_KEY } });

AWS Lambda

const { withLambdaObservability } = require('auto-api-observe');

const handler = async (event) => ({ statusCode: 200, body: 'ok' });
module.exports.handler = withLambdaObservability(handler, { apiKey: process.env.APILENS_KEY });

tRPC

import { createTrpcObservabilityMiddleware } from 'auto-api-observe';

const observability = createTrpcObservabilityMiddleware({ apiKey: process.env.APILENS_KEY });

export const observedProcedure = t.procedure.use(observability);

What's Logged

Every request emits a structured JSON entry:

{
  "timestamp": "2026-04-28T12:00:00.000Z",
  "traceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "method": "GET",
  "route": "/api/users/:id",
  "path": "/api/users/42",
  "status": 200,
  "latency": 85,
  "latencyMs": "85ms",
  "slow": false,
  "ip": "127.0.0.1",
  "userAgent": "Mozilla/5.0",
  "requestSize": 512,
  "responseSize": 1024,
  "tags": { "service": "user-api", "env": "production" },
  "dbCalls": {
    "calls": 2,
    "totalTime": 45,
    "slowestQuery": 30,
    "queries": [
      { "query": "SELECT * FROM users WHERE id = ?", "source": "pg", "queryTime": 30 },
      { "query": "SELECT COUNT(*) FROM sessions WHERE user_id = ?", "source": "pg", "queryTime": 15 }
    ]
  },
  "outboundCalls": [
    { "method": "POST", "url": "https://api.stripe.com/v1/charges", "status": 200, "latency": 340 }
  ]
}

Auto DB Instrumentation

No code changes. The middleware patches these libraries at startup:

| Library | What's tracked | |---------|---------------| | pg (node-postgres) | SQL query, masked params, execution time | | mysql2 | Same | | mongoose | Operation, collection, execution time | | @prisma/client | Model, action, execution time | | knex | SQL query, execution time | | sequelize | SQL query, execution time | | ioredis | Command, execution time | | better-sqlite3 | SQL query, execution time | | node-redis | Command, execution time |

For each query: masked SQL (values replaced with ?), execution time in ms, source library name, per-request aggregates.

app.get('/orders', async (req, res) => {
  const orders = await db.query('SELECT * FROM orders WHERE user_id = $1', [req.user.id]);
  res.json(orders);
  // log shows: dbCalls: { calls: 1, totalTime: 12, queries: [...] }
});

Outbound HTTP Tracking

Automatically tracks all outbound HTTP calls your server makes — fetch, axios, and undici:

observe({
  apiKey: 'sk_live_...',
  autoInstrumentOutbound: true,  // default: true
});

Each outbound call is captured per-request:

"outboundCalls": [
  { "method": "GET",  "url": "https://api.github.com/user", "status": 200, "latency": 120 },
  { "method": "POST", "url": "https://api.stripe.com/v1/charges", "status": 201, "latency": 340 }
]

Sensitive query parameters (token, api_key, password, secret, etc.) are automatically stripped from URLs.


Sensitive Field Masking

Any field you attach via addField() with a sensitive name is automatically redacted before shipping:

addField('userId', 'u_123');          // shipped as-is
addField('authorization', 'Bearer x'); // shipped as "[REDACTED]"

Masked keys (case-insensitive): authorization, password, token, api_key, cookie, secret, credit_card, ssn, private_key, and more.


Global Tags

Attach metadata to every log entry for filtering in the dashboard:

observe({
  apiKey: 'sk_live_...',
  tags: {
    service: 'user-api',
    env: process.env.NODE_ENV,
    region: 'us-east-1',
    version: '2.4.1',
  },
});

Process Metrics

Ship memory, CPU, and uptime metrics on an interval (default: every 30s):

observe({
  apiKey: 'sk_live_...',
  processMetrics: 30000,  // ms interval, or false to disable
});

Each interval reports: rss, heapUsed, heapTotal, external, CPU usage, load average, free memory.


Unhandled Error Capture

Catch and ship uncaughtException and unhandledRejection events:

observe({
  apiKey: 'sk_live_...',
  captureUnhandledErrors: true,
});

Cloud Dashboard

Sign up free at apilens.rest — no credit card required.

What you see:

  • Overview — total requests, error rate, P95 latency, 6 interactive charts
  • All Requests — every request with full DB query details, trace IDs, filters
  • Routes — per-route breakdown (calls, avg latency, P95, errors, slow count)
  • Errors — paginated 4xx/5xx log with error timeline and top error routes
  • Slow Requests — latency distribution histogram and worst offenders
  • Database — query profiling, N+1 detection, slow queries, source distribution
  • Outbound — third-party API latency, error rates, call frequency
  • Traces — distributed trace waterfall visualization
  • Live Tail — real-time SSE stream with method/status/route filters
  • Usage — daily quota tracking
  • Alerts — email or Slack when error rate or latency spikes

Screenshots

Overview — real-time KPIs, request volume, latency percentiles, status distribution

Overview

Request Log — every request with full DB query details, trace IDs, filters

Request Log

Database Profiling — N+1 detection, slow queries, source distribution

Database

Routes — per-route breakdown with latency, errors, slow count

Routes


All Options

observe({
  // Required
  apiKey: 'sk_live_...',          // get one free at apilens.rest

  // Request tracking
  slowThreshold: 1000,            // ms — flag requests above this (default: 1000)
  skipRoutes: ['/health'],        // skip routes — string prefix or RegExp
  traceHeader: 'x-trace-id',     // header for trace ID propagation
  sampleRate: 1.0,                // 0.0–1.0, fraction to log (default: 1.0)
  maxRoutes: 1000,                // cap on distinct routes in metrics (default: 1000)

  // Callbacks
  onRequest: (ctx) => {},         // called at request start with context
  onResponse: (entry) => {},      // called after response with log entry

  // Logging
  logger: console.log,            // custom log fn, or false to silence
  tags: { service: 'api' },       // global tags on every entry

  // DB instrumentation
  autoInstrument: true,           // auto-patch DB libraries (default: true)

  // Outbound HTTP
  autoInstrumentOutbound: true,   // track fetch/axios/undici calls (default: true)

  // Process monitoring
  processMetrics: 30000,          // interval ms, or false to disable (default: 30000)
  captureUnhandledErrors: false,  // capture uncaughtException/unhandledRejection

  // Cloud shipper
  endpoint: 'https://...',        // override for self-hosted (default: api.apilens.rest)
  flushInterval: 5000,            // ms between batch flushes (default: 5000)
  flushSize: 100,                 // flush when queue hits this size (default: 100)
});

Custom Fields

const { addField } = require('auto-api-observe');

app.get('/orders', async (req, res) => {
  addField('userId', req.user.id);
  addField('plan', req.user.plan);
  const orders = await Order.findAll({ where: { userId: req.user.id } });
  res.json(orders);
});

In-Memory Metrics

Access per-route aggregates without sending anything to the cloud:

const { getMetrics } = require('auto-api-observe');

app.get('/internal/metrics', (req, res) => res.json(getMetrics()));

Returns: count, avg/min/max latency, error count, slow count, status code distribution — per route.


Distributed Tracing

Trace IDs propagate automatically across services via the x-trace-id header:

Service A (generates traceId: abc-123)
  → calls Service B (reads x-trace-id, reuses abc-123)
    → calls Service C (same ID — full chain visible in logs)

Access in your handler:

  • Express/Fastify: req.traceId
  • All frameworks: getContext()?.traceId

TypeScript

import observe, {
  fastifyObservability,
  koaObservability,
  honoObservability,
  withLambdaObservability,
  createTrpcObservabilityMiddleware,
  ObservabilityOptions,
  LogEntry,
  RequestContext,
  OutboundCall,
  addField,
  getContext,
  getMetrics,
  autoInstrument,
  recordOutboundCall,
} from 'auto-api-observe';

Comparison

| Feature | auto-api-observe | Datadog | New Relic | Sentry | |---------|:---:|:---:|:---:|:---:| | Setup time | 10 seconds | 30+ min | 30+ min | 15+ min | | Lines of code | 1 | 20+ | 15+ | 10+ | | Runtime dependencies | 0 | 50+ | 40+ | 30+ | | Frameworks supported | 12 | agent-based | agent-based | SDK per framework | | Auto DB tracking | 9 libraries | custom setup | custom setup | limited | | Outbound HTTP tracking | auto | auto | auto | manual | | Process metrics | built-in | agent | agent | no | | Free tier | free during beta | 14-day trial | 100 GB/mo | 5k events |


Contributing

git clone https://github.com/rahhuul/auto-api-observe.git
cd auto-api-observe
npm install
npm test       # 107 tests across 10 files
npm run build  # TypeScript compile check

Open an issue before submitting large changes.


License

MIT