How to Monitor Your Express.js API in 60 Seconds — No Config Required
Most API monitoring guides need Prometheus, Grafana, and an afternoon. Here's how to get full observability with one line of middleware — latency, errors, DB queries, and traces.
The Problem With API Monitoring Today
You've built an Express API. It works. It's deployed. Then someone asks: "Which endpoints are slow? What's our error rate? How many database queries does that route make?"
Your options:
- Datadog — $23/host/month, 30+ minutes to configure, requires an agent
- New Relic — needs a credit card to start, complex setup
- Prometheus + Grafana — free, but half a day to wire up
- console.log — the classic. Works until it doesn't
What if you could get structured logs, latency tracking, error rates, DB query profiling, and distributed traces — with one line of code and zero configuration?
The 60-Second Setup
Step 1: Install (10 seconds)
npm install auto-api-observe
Step 2: Add one line (5 seconds)
const express = require('express');
const observe = require('auto-api-observe');
const app = express();
app.use(observe()); // That's it. Done.
app.get('/api/users', async (req, res) => {
const users = await db.query('SELECT * FROM users');
res.json(users);
});
app.listen(3000);
Step 3: Hit any endpoint (5 seconds)
curl http://localhost:3000/api/users
That's it. Every request now outputs a structured JSON log with everything you need.
What You Get Automatically
Every single request produces this:
{
"timestamp": "2026-04-08T10:30:00.000Z",
"traceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"method": "GET",
"route": "/api/users",
"status": 200,
"latency": 85,
"latencyMs": "85ms",
"ip": "127.0.0.1",
"slow": false,
"dbCalls": {
"calls": 1,
"totalTime": 42,
"slowestQuery": 42,
"queries": [
{
"query": "SELECT * FROM users",
"source": "pg",
"queryTime": 42
}
]
}
}
No configuration. No environment variables. No YAML files. No agents.
Here's what each field gives you:
| Field | What It Tells You |
|---|---|
method + route | Which endpoint was called |
status | Success or failure (4xx/5xx) |
latency | How long the request took (ms) |
traceId | Unique ID to follow requests across services |
dbCalls.calls | How many database queries this request made |
dbCalls.totalTime | Time spent in the database (ms) |
dbCalls.slowestQuery | Your bottleneck query time |
dbCalls.queries | Every query with masked SQL and timing |
slow | Flagged if latency exceeds threshold (default: 1000ms) |
Auto DB Instrumentation — Zero Code Changes
This is the part that saves hours. The middleware automatically patches 8 database libraries at the driver level:
- pg (node-postgres)
- mysql2
- mongoose (MongoDB)
- @prisma/client (Prisma)
- knex
- sequelize
- ioredis (Redis)
- better-sqlite3
You don't configure anything. Just use your database libraries as normal:
// Your code stays exactly the same
app.get('/api/orders', async (req, res) => {
const orders = await db.query('SELECT * FROM orders WHERE user_id = $1', [req.user.id]);
const products = await db.query('SELECT * FROM products WHERE id = ANY($1)', [orders.map(o => o.product_id)]);
res.json({ orders, products });
});
// Output automatically shows: dbCalls: { calls: 2, totalTime: 67, queries: [...] }
How does it work? AsyncLocalStorage creates a per-request context. Prototype monkey-patching intercepts queries at the driver level — not the ORM level. This means it works with Prisma, Sequelize, TypeORM, Drizzle, Knex, or raw SQL. No per-ORM configuration needed.
Query values are automatically masked ($1 becomes ?) for security. The overhead is ~50 nanoseconds per DB call — negligible compared to actual query time.
Spot Problems Before Users Do
Find Slow Endpoints
Any request exceeding the threshold (default: 1000ms) gets flagged:
{
"route": "/api/reports/monthly",
"latency": 3200,
"slow": true,
"dbCalls": { "calls": 12, "totalTime": 2900 }
}
You can immediately see: this endpoint is slow because it makes 12 DB queries taking 2.9 seconds.
Detect N+1 Queries
When dbCalls.calls is suspiciously high for a GET request, you've found an N+1:
{
"route": "/api/orders",
"latency": 1850,
"dbCalls": { "calls": 51, "totalTime": 1720 }
}
51 database calls? That's 1 query for orders + 50 queries for each order's product. Classic N+1 problem. Fix it with a JOIN or eager loading, and watch dbCalls.calls drop to 2.
*Want a deep dive on N+1 detection?* Read our complete N+1 query guide.
Track Error Rates
Filter your logs by status >= 400 to see all errors. Group by route to find which endpoints fail most.
{
"route": "/api/payments",
"status": 500,
"latency": 45,
"error": "Connection refused"
}
Distributed Tracing Across Services
Every request gets a unique traceId. If your frontend calls Service A, which calls Service B, the same trace ID follows the entire chain:
Client → Service A (traceId: abc-123)
→ Service B (reads x-trace-id header, reuses abc-123)
→ Service C (same ID — full chain visible)
Access it in your handler with req.traceId. No Jaeger setup required.
Add a Cloud Dashboard (Optional)
Structured logs are powerful. But if you want charts, alerting, and a team-accessible UI — add your API key:
app.use(observe({
apiKey: process.env.APILENS_KEY, // free at apilens.rest
}));
Now you get a full dashboard at apilens.rest:
- Overview — request volume, error rate, P95 latency charts
- Routes — per-route breakdown (avg latency, errors, slow count)
- Database — N+1 detection, slow queries, query source distribution
- Errors — every 4xx/5xx with timeline and filters
- Live Tail — real-time SSE stream of requests as they happen
- Alerts — email/Slack when error rate or latency spikes
Free during beta. No credit card.
Customize When You Need To
The defaults work for most apps. But you can configure everything:
app.use(observe({
slowThreshold: 500, // flag requests over 500ms (default: 1000ms)
skipRoutes: ['/health', '/metrics'], // skip health checks
sampleRate: 0.5, // log 50% of requests (for high traffic)
maxRoutes: 500, // cap distinct routes in memory metrics
logger: false, // silence console output (use with apiKey only)
autoInstrument: true, // auto-patch DB libraries (default: true)
}));
Add Custom Fields
Attach business context to any request:
const { addField } = require('auto-api-observe');
app.get('/api/orders', async (req, res) => {
addField('userId', req.user.id);
addField('plan', req.user.plan);
const orders = await getOrders(req.user.id);
addField('orderCount', orders.length);
res.json(orders);
});
Get Aggregated Metrics
const { getMetrics } = require('auto-api-observe');
app.get('/metrics', (req, res) => res.json(getMetrics()));
Returns per-route aggregates: request count, avg/min/max latency, error count, slow request count, and status code distribution.
How It Compares
| Feature | auto-api-observe | Datadog | New Relic | Prometheus + Grafana |
|---|---|---|---|---|
| Setup time | 60 seconds | 30+ minutes | 30+ minutes | 2-4 hours |
| Lines of code | 1 | 20+ | 15+ | 100+ |
| Dependencies | 0 | 50+ | 40+ | 5+ packages |
| Config files | 0 | Agent YAML + app config | Config file + license | prometheus.yml + grafana dashboards |
| Auto DB tracking | 8 libraries | Custom setup per lib | Custom setup | Manual instrumentation |
| N+1 detection | Built-in | Manual query analysis | Manual | Manual |
| Distributed tracing | Automatic | Requires agent + config | Requires agent | Requires Jaeger/Zipkin |
| Cost | Free (beta) | $23/host/month | $25+/host | Free (self-hosted infra cost) |
| Vendor lock-in | None (MIT open-source) | Yes | Yes | No |
Works With Fastify Too
const fastify = require('fastify')();
const { fastifyObservability } = require('auto-api-observe');
await fastify.register(fastifyObservability);
fastify.get('/api/users', async () => ({ users: [] }));
await fastify.listen({ port: 3000 });
Same structured output. Same auto DB tracking. Same zero config.
Summary
1. Install — npm install auto-api-observe
2. Add one line — app.use(observe())
3. Get instant visibility — latency, errors, DB queries, traces, slow endpoint detection
No Prometheus. No Grafana. No agents. No YAML. No credit card.
Every request becomes a structured JSON log. Every database query is tracked automatically. Every slow endpoint gets flagged. And if you want charts and alerting, add an API key for the free cloud dashboard.
Try it now
npm install auto-api-observe
- Dashboard: apilens.rest
- GitHub: github.com/rahhuul/auto-api-observe
- npm: auto-api-observe
Zero dependencies. 44 tests. MIT licensed. Free during beta.
*What API monitoring setup do you use for Express apps? I'd love to hear what works for you.*