server-devtools
v0.1.6
Published
Chrome DevTools for your backend. Inspect HTTP requests, database operations, outbound calls, errors, and execution timing from an embedded dashboard.
Maintainers
Readme
ServerDevTools
Chrome DevTools for your backend.
ServerDevTools is an embeddable backend inspector for Node.js applications: Chrome DevTools for your Node.js backend.
It gives you a live dashboard for inspecting:
- incoming HTTP requests
- request headers, params, query, and body
- response status, headers, and body
- execution flow
- framework middleware and handlers
- outgoing HTTP requests
- database operations
- errors
- timing
- current/authenticated user context
- server-side request search
- request filtering by HTTP method and status
Instead of jumping between logs, traces, and multiple observability tools, ServerDevTools lets you inspect what your backend is doing from one developer-focused interface.
Demo

POST /users
│
├── Express middleware
├── Request handler
├── MongoDB query
└── External HTTP requestOpen:
http://localhost:3000/_devtoolsand inspect the complete request lifecycle.
Why ServerDevTools?
Frontend developers have Chrome DevTools.
Backend debugging often looks like this:
console.log(...)
tail logs
search logs
inspect database
check external API logs
open tracing platform
repeatServerDevTools provides a simpler workflow:
Run request
↓
Open /_devtools
↓
Inspect what happenedIt is designed for live backend debugging, not as a replacement for full production observability platforms.
Installation
npm install server-devtoolsServerDevTools includes the supported OpenTelemetry instrumentation libraries; you do not need to install the database instrumentation packages separately.
Quick Start
ServerDevTools uses an authenticated dashboard and stores inspection history in
./server-devtools.db by default, relative to the process working directory.
Keep dashboard credentials and any encryption key in environment variables.
The encryption key must be a base64-encoded 32-byte key. Generate one with:
node -e 'console.log(require("node:crypto").randomBytes(32).toString("base64"))'fields is optional. By default, ServerDevTools encrypts these field names:
passwordaccessTokenrefreshTokenauthorizationcookie
Specifying fields replaces the default list; it does not extend it.
To customize the encrypted fields:
encryption: {
key: process.env.SERVER_DEVTOOLS_ENCRYPTION_KEY!,
fields: ["password", "authorization", "apiKey"],
},HTTP header redaction is enabled by default for sensitive header names such
as authorization, proxy-authorization, cookie, set-cookie, and
x-api-key. Add application-specific header names without replacing the
defaults:
redaction: {
additionalHeaders: ["x-access-token", "client-secret"],
},Header redaction applies to header names only. It does not redact body fields, URLs, query parameters, or arbitrary span attributes.
getCurrentUser is optional. Use it when your application already exposes the
authenticated user on the request; ServerDevTools does not require a
Passport, JWT, or other authentication framework.
Node.js / Express TypeScript
Create server-devtools.ts:
import ServerDevTools from "server-devtools";
export const devtools = new ServerDevTools({
auth: {
username: process.env.SERVER_DEVTOOLS_USERNAME!,
password: process.env.SERVER_DEVTOOLS_PASSWORD!,
},
encryption: {
key: process.env.SERVER_DEVTOOLS_ENCRYPTION_KEY!,
},
getCurrentUser: (req) => req.user,
});
await devtools.start();// src/main.ts
import { createServer } from "node:http";
import { devtools } from "../server-devtools";
const server = createServer((req, res) => {
devtools.middleware(req, res, () => {
res.statusCode = 404;
res.end("Not found");
});
});
server.listen(3000);tsx watch --import ./server-devtools.ts src/main.tsData handling and privacy
ServerDevTools is an in-process developer inspector. Depending on the active
instrumentation, request context, and framework integration, it can persist
incoming request summaries and selected headers, paths and query attributes,
parsed request bodies, response status/headers/bodies, database and Redis
spans, outbound HTTP details, span errors and stack traces, and current-user
data when getCurrentUser is configured. A category is not guaranteed for
every request: preload order, active request context, framework parsers, and
instrumentation-specific hooks determine what is available.
The only trace persistence provider currently implemented is local SQLite at
./server-devtools.db; the supported database and Redis instrumentations are
capture sources, not telemetry storage providers. The SDK does not send
telemetry to a ServerDevTools service or require an OpenTelemetry Collector.
Use Clear History or delete an individual trace to remove trace data.
There is no automatic trace retention or TTL. Dashboard sessions are stored
separately, expire after 24 hours, and are removed on logout or expiry lookup.
Header-name redaction is enabled by default before captured HTTP details and
HTTP span attributes are persisted. The defaults are authorization,
proxy-authorization, cookie, set-cookie, and x-api-key; redaction's
additionalHeaders extends that list. Names are trimmed, matched
case-insensitively, and deduplicated. Redaction does not cover body fields,
URLs, query parameters, or arbitrary non-header span attributes, and it does
not rewrite existing records.
Optional AES-256-GCM encryption protects configured field names in supported metadata and outbound-detail records at rest. It is separate from redaction, does not encrypt the whole database or all span attributes, and authorized dashboard/API reads decrypt covered values. See the configuration reference, encryption guide, and security guidance for the exact coverage.
The fixed 256 KiB limit applies independently to native outbound request and response body capture. An oversized body is skipped rather than truncated; headers and the original network payload are preserved. Inbound response and fetch body capture do not currently have an SDK byte limit. Capture buffers or clones data, and persistence can add synchronous work; capture failures are isolated where implemented, but do not assume zero overhead or non-blocking behavior. Treat captured data and the embedded dashboard as sensitive in any shared or production-like environment.
await devtools.start() automatically registers OpenTelemetry's ESM loader hook
once per process before starting the OpenTelemetry SDK. Keep startup in the
preload module and let it complete before application dependencies load, including
normal Redis (ioredis) imports. Already-loaded dependencies cannot be instrumented
retroactively.
The installed OpenTelemetry hook exports asynchronous loader hooks, which require
node:module.register(). Newer Node.js versions deprecate this API; its
registerHooks() replacement takes synchronous hooks and cannot directly load
OpenTelemetry's hook.mjs. This compatibility requirement does not change the
startup command or configuration.
Why is initialization asynchronous?
await devtools.start() waits for the instrumentation startup contract. In the
currently installed OpenTelemetry SDK, NodeSDK.start() performs registration
synchronously; ServerDevTools then enables fetch capture before start()
resolves. The async API keeps startup compatible with instrumentation work that
may need to become asynchronous without changing application setup. It does not
wait for application requests, database connections, or an external telemetry
service.
Call it from the preload module before importing application dependencies. The instrumentations patch supported HTTP, framework, database, Redis, and Undici modules as they load, so modules imported first cannot be instrumented retroactively. Preloading adds startup work and startup failures can prevent the application from starting, but it ensures capture is installed before traffic begins.
The application can import the same instance normally and mount it in Express:
// src/main.ts
import express from "express";
import { devtools } from "../server-devtools";
const app = express();
app.use((req, res, next) => devtools.middleware(req, res, next));
app.get("/users/:id", async (req, res) => {
res.json({ id: req.params.id, name: "John Doe" });
});
app.listen(3000);Node.js / Express JavaScript
For JavaScript projects, create server-devtools.js:
const ServerDevTools = require("server-devtools");
const devtools = new ServerDevTools({
auth: {
username: process.env.SERVER_DEVTOOLS_USERNAME,
password: process.env.SERVER_DEVTOOLS_PASSWORD,
},
encryption: {
key: process.env.SERVER_DEVTOOLS_ENCRYPTION_KEY,
},
getCurrentUser: (req) => req.user,
});
module.exports = { devtools };
void devtools.start();Preload the JavaScript file normally:
node --import ./server-devtools.js src/main.jsIn the application, import the same instance and mount it with:
const express = require("express");
const { devtools } = require("../server-devtools.js");
const app = express();
app.use((req, res, next) => devtools.middleware(req, res, next));
app.listen(3000);JavaScript projects do not need allowJs.
NestJS
NestJS uses a different preload pattern. Create a plain server-devtools.js
outside src:
const ServerDevTools = require("server-devtools");
const devtools = new ServerDevTools({
auth: {
username: process.env.SERVER_DEVTOOLS_USERNAME,
password: process.env.SERVER_DEVTOOLS_PASSWORD,
},
encryption: {
key: process.env.SERVER_DEVTOOLS_ENCRYPTION_KEY,
},
getCurrentUser: (req) => req.user,
});
globalThis.__serverDevtools = devtools;
void (async () => {
await devtools.start();
})();Enable JavaScript files in the Nest TypeScript configuration:
{
"compilerOptions": {
"allowJs": true
}
}Start Nest with the preload file:
NODE_OPTIONS='--import ./server-devtools.js' nest start --watchDo not create another ServerDevTools instance in main.ts. Retrieve the
preloaded instance instead:
// src/main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { ServerDevToolsNestInterceptor } from "server-devtools/nestjs";
import ServerDevTools from "server-devtools";
declare global {
var __serverDevtools: ServerDevTools | undefined;
}
async function bootstrap() {
const devtools = globalThis.__serverDevtools;
if (!devtools) {
throw new Error(
"ServerDevTools was not preloaded. Start Nest with NODE_OPTIONS='--import ./server-devtools.js'.",
);
}
const app = await NestFactory.create(AppModule);
app.use((req, res, next) => devtools.middleware(req, res, next));
app.useGlobalInterceptors(
new ServerDevToolsNestInterceptor(),
);
await app.listen(3000);
}
bootstrap();This Nest-specific pattern initializes instrumentation before Nest and application dependencies load, while avoiding a second ServerDevTools initialization during watch mode.
Each example imports and mounts the same instance that was started by the
preloaded initialization file. Its middleware serves the dashboard from the
application's existing HTTP server at http://localhost:<your-port>/_devtools.
OpenTelemetry and outbound fetch capture start when devtools.start() is
called; constructing the instance alone does not wrap globalThis.fetch.
Then open:
http://localhost:3000/_devtoolsMake requests to your application and they will appear in the dashboard.
Examples
Working example applications are available if you want to see ServerDevTools integrated into a real project.
Express + TypeScript
A minimal Express + TypeScript application showing ServerDevTools initialization, preloading, middleware mounting, encryption configuration, current-user capture, and request inspection.
NestJS
A minimal NestJS application showing the ServerDevTools preload setup, middleware integration, NestJS error interceptor, encryption configuration, current-user capture, and request inspection.
Request Inspector
Inspect the incoming request, including:
- method
- path
- route
- headers
- query parameters
- route parameters
- request body
Example:
{
"name": "John Doe",
"email": "[email protected]"
}Response Inspector
Inspect what your application returned:
- status code
- response headers
- response body
Example:
{
"received": {
"name": "John Doe",
"email": "[email protected]"
}
}Execution View
ServerDevTools reconstructs the execution hierarchy for each request.
POST /users
│
├── Express middleware
├── Request handler
├── MongoDB query
└── HTTP request
└── api.example.comThis makes it easier to understand what your backend executed during a request and where time was spent.
How It Works
ServerDevTools uses OpenTelemetry instrumentation to observe supported backend operations.
Application request and dependencies
↓
OpenTelemetry captures supported activity
↓
ServerDevTools maps captured spans and details
↓
Historical inspection data in SQLite
↓
Embedded dashboard on the application's HTTP serverOpenTelemetry provides the instrumentation layer. ServerDevTools provides the developer-focused inspection experience on top of it.
Embedded Dashboard
ServerDevTools does not require a separate dashboard server. It mounts directly into your existing application:
http://localhost:3000
│
├── /api/users
├── /api/orders
├── /api/...
└── /_devtoolsCurrent Support
ServerDevTools currently supports:
- Node.js
- Express
- NestJS middleware and error interception
- MongoDB
- PostgreSQL through
pg - MySQL through
mysql2 - Redis (ioredis)
- native HTTP/HTTPS and fetch/Undici requests, including headers and bodies
- request search and filtering by HTTP method and status
- current/authenticated user context when supplied by the application
- OpenTelemetry-powered activity capture
Database tracing
Supported database operations automatically appear inside the request execution trace when using these supported drivers:
- MongoDB
- PostgreSQL (
pg) - MySQL (
mysql2) - Redis (ioredis)
Redis command spans include command inspection, arguments, timings, request links, and command errors. Redis (ioredis) support has been verified in Express and NestJS applications, including pipelines and transactions.
What ServerDevTools Is Not
ServerDevTools is not intended to replace full observability, monitoring, alerting, or log aggregation platforms.
Its focus is:
Interactive inspection of individual backend requests.
Development Status
ServerDevTools is currently under active development.
The first release focuses on making this workflow excellent:
Install ServerDevTools
↓
Initialize + preload ServerDevTools
↓
Start application
↓
Open /_devtools
↓
Run and inspect requestsExpect APIs and configuration to evolve before 1.0.
Coming next
Planned, not yet available:
- Configurable trace storage, including storing ServerDevTools data through user-configured storage instead of only the default local SQLite storage
Security
ServerDevTools may capture sensitive application data, including request headers, request bodies, response bodies, database operations, and external request metadata.
Optional encryption at rest can protect configured fields in supported
captured data. It does not redact all captured data or replace application
security practices. The dashboard has authentication; use strong credentials
and restrict access to /_devtools through your network and deployment setup.
Header-name redaction is available for configured HTTP headers, but body, URL, query-parameter, and arbitrary span-attribute redaction are not implemented. Capture-size limits apply only where documented by the SDK.
Philosophy
ServerDevTools should remain:
- easy to install
- easy to understand
- framework-friendly
- developer-focused
- useful without requiring a full observability stack
The goal is simple:
Make backend debugging feel more like using browser DevTools.
Contributing
The project is still early.
Redis (ioredis) integration tests
The ioredis integration tests require a disposable Redis instance. Run them with its connection URL:
REDIS_TEST_URL=redis://127.0.0.1:6379 npx tsx --test tests/ioredis-integration.test.tsWhen REDIS_TEST_URL is not set, the Redis integration tests are
reported as skipped. The tests use isolated keys and clean up their
clients and data.
Issues, ideas, bug reports, and contributions are welcome.
License
Apache-2.0
