Dumpio

Dokumentace

The dumpio-client package (Node ≥ 16, CommonJS and ESM) sends faithful, typed value dumps and structured messages to Dumpio over HTTP. It serializes plain objects and class instances (by constructor name), Map/Set/Date/RegExp/Error/typed arrays, bigint, and functions into the language-agnostic value tree, breaks cycles with ref nodes, and captures the calling file:line automatically. ORM models render as their loaded columns rather than raw internals (Sequelize via dataValues, Mongoose via toObject() — only already-loaded data, no lazy query triggered). Middleware is provided for Express, Koa, and Fastify.

1npm install dumpio-client

Every helper is fire-and-forget and never throws into your app — if the viewer isn't running, the dump is dropped silently. The functions return a Promise that you usually don't need to await.

Why send dumps to Dumpio instead of console.log

  • It doesn't flood stdout. The dump travels over the network to a separate window, so you don't have to spew objects into a production server's log or a test runner's output.
  • It preserves types and structure. Instead of [object Object] you see a typed tree — classes, Map/Set, bigint, cycles via ref.
  • It sorts things for you. Color flags, labels, and channels separate output from different places.
  • It won't crash the process. Everything is wrapped so a send error (e.g. when the viewer isn't running) can never leak into your app.

For the full list of dump types and how Dumpio renders them, see Dump types; if nothing shows up, start with Troubleshooting.

Quick start

1const { dumpio, dd, configure } = require('dumpio-client')
2// ESM: import { dumpio, dd, configure } from 'dumpio-client'
3 
4configure({ host: 'localhost', port: 21234 }) // optional — these are the defaults
5 
6dumpio(user) // dump a variable
7dumpio(user, { label: 'user', flag: 'green' }) // with a label and flag
8dd(a, b) // dump every argument, wait for the send, and exit the process

Which entry point should you use?

I want to… Use
Quickly dump a value dumpio(x)
Add a flag / label / channel dumpio(x, { label, flag, channel })
Dump and stop the process dd(x)
Send something other than a variable (exception, query, HTTP…) typed helpers dumpioException(), dumpioQuery(), …
Wait for sends to finish before a script exits await flush()

Configuration

Defaults are read from the environment, or you can set them via configure({...}):

Option Env var Default
host DUMPIO_HOST localhost
port DUMPIO_PORT 21234
path /dumps
token DUMPIO_TOKEN ""
timeoutMs 1500
enabled DUMPIO_DISABLE (set it to turn off) true
maxDepth 6
maxItems 100
maxString 2000
1const { config } = configure({ host: '127.0.0.1', port: 21234, token: 'Dio-…' })

The token is sent as the X-Dumpio-Token header. configure() returns the resulting config (you can also reach the exported config object). The SDK speaks HTTP only (no TCP).

Core functions

Function Purpose
dumpio(value, options?): Promise<void> Send a single variable dump; options = { label?, flag?, channel?, timestamp? }
dd(...values): void Dump every argument, wait for the send (flush), and exit the process
configure(options): DumpioConfig Override config and return the result
serialize(value, limits?): VarNode Return the typed tree without sending (useful for custom envelopes)
flush(): Promise<void> Wait until all in-flight dumps have been sent (always resolves)

flush() is handy before a short script exits, so the last dumps make it out:

1await Promise.all([dumpio(a), dumpioQuery(sql, binds)])
2await flush()

Fluent builder & flood control

dio(value) returns a chainable builder that mirrors the PHP client. It ships on .send() or automatically at the end of the current tick (JS has no destructors), so .send() is optional.

1const { dio, ddio, stopwatch } = require('dumpio-client')
2 
3dio(user).red().label('user').channel('auth') // auto-sends
4dio(payload).green().send() // explicit
5 
6dio(state).when(debug) // send only if truthy (.unless() is the opposite)
7 
8// Keep loops from flooding the viewer (keyed by call site, or a shared name):
9for (const row of rows) {
10 dio(row).once() // only the first iteration
11 dio(row).limit(5) // at most 5
12 dio(row).count('rows') // one live-updating "×N" entry
13}
14 
15ddio(a, b) // dump every argument, flush, then exit the process

Colors: red() yellow() blue() gray() purple() pink() green() (or flag('…')), plus label(), channel(), when()/unless(), once()/limit(n)/count(name?), send().

Timing — stopwatch

1const sw = stopwatch('import')
2// … work …
3sw.lap('parsed') // split time, keeps running
4// … more work …
5sw.stop('done') // final time

Each lap()/stop() sends a measure dump with the elapsed milliseconds and the heap-memory delta since the stopwatch was created.

Typed helpers

Each renders a dedicated view in the viewer, sets schemaVersion: 1, and picks a sensible default flag. The last argument is always opts = { flag?, channel?, timestamp? }.

Helper Default flag Renders
dumpio(v, { label }) blue faithful var tree
dumpioMessage(msg, extra?, opts?) blue generic titled data
dumpioException(err, context?, opts?) red error + stack trace
dumpioQuery(sql, bindings?, timeMs?, opts?) purple SQL query (opts.connection, opts.message)
dumpioHttp({ method, url, status?, headers?, body?, responseTime? }, opts?) by status HTTP request
dumpioLog(level, msg, details?, opts?) by level log
dumpioModel(class, { attributes, relations?, exists?, connection? }, opts?) one domain object
dumpioCollection(items, opts?) list (opts.message)
dumpioTable(columns, rows, opts?) explicit table (opts.message)
dumpioMeasure(name, timeMs, { memory?, context? }?, opts?) one timing
dumpioPerformance({ metrics, breakdown?, context? }, opts?) a metric bundle
dumpioEvent(event, { entity?, entityId?, actor?, data?, metadata? }?, opts?) a domain event
dumpioHtml(html, { message?, title? }?) an HTML fragment/page in a sandboxed iframe
dumpioMail({ html?, text?, subject?, from?, to?, cc?, bcc?, attachments? }, opts?) a rendered email (envelope + body)

Flag by HTTP status: undefined → blue, ≥ 500 → red, ≥ 400 → yellow, ≥ 300 → blue, else green. Flag by log level: emergency/alert/critical/error → red, warning/notice → yellow, info → blue, else gray.

Examples

1const {
2 dumpioException, dumpioQuery, dumpioHttp,
3 dumpioLog, dumpioTable, dumpioEvent,
4} = require('dumpio-client')
5 
6// An error with a structured stack trace and context
7try {
8 await gateway.charge(order)
9} catch (err) {
10 dumpioException(err, { orderId: order.id, user: user.email })
11 throw err
12}
13 
14// A database query with parameters and time
15dumpioQuery('select * from orders where status = ?', ['paid'], 12.4)
16 
17// An outgoing HTTP call — the flag is derived from the status
18dumpioHttp({ method: 'POST', url: 'https://api.stripe.com/v1/charges', status: 402, responseTime: 240 })
19 
20// A log line with details (flag by level)
21dumpioLog('error', 'Payment failed', { gateway: 'stripe', code: 'card_declined' })
22 
23// A table — columns + rows
24dumpioTable(['id', 'status'], [[1, 'paid'], [2, 'pending']], { message: 'Orders' })
25 
26// A business event with an actor and data
27dumpioEvent('order.refunded', { entity: 'Order', entityId: 42, actor: user.email, data: { amount: 1990 } })

For a detailed look at how Dumpio renders each type (and how it parses exceptions), see Dump types and Exceptions.

Middleware for web frameworks

The middleware times each request and, when the response finishes, sends an http dump. It is defensive — it can never crash the host app. Import it from dumpio-client/middleware.

1const express = require('express')
2const { expressMiddleware, installGlobalErrorHandlers } = require('dumpio-client/middleware')
3 
4const app = express()
5app.use(expressMiddleware({ channel: 'http' }))
6 
7// Uncaught errors / rejected promises as `exception` dumps.
8installGlobalErrorHandlers({ channel: 'errors' })

Middleware options: { channel?, headers?, skip? }headers includes request headers (off by default), skip(req) => boolean filters out requests (e.g. health-check pings).

Koa and Fastify have their own integrations with the same options:

1const { koaMiddleware, fastifyPlugin } = require('dumpio-client/middleware')
2 
3app.use(koaMiddleware()) // Koa
4fastify.register(fastifyPlugin) // Fastify

installGlobalErrorHandlers() returns an unregister function and does not swallow errors — your existing handlers and the process's default behavior still run.

Auto-capture outbound HTTP

captureHttp() patches node:http / node:https so every outgoing request your app makes — including axios, got and node-fetch, which all funnel through them — is forwarded to the viewer as an http dump. No per-call code needed.

1const { captureHttp } = require('dumpio-client')
2 
3const stop = captureHttp({ channel: 'outbound' })
4// ... your app makes HTTP calls ...
5stop() // restore the originals when you're done

It's idempotent, defensive (a capture failure never disturbs the request), and skips requests to the Dumpio viewer itself, so the SDK never dumps its own traffic. Pass ignore: (info) => boolean to skip noisy endpoints — info carries { method, url, host, port }.

Node's global fetch (undici) uses its own socket stack, not node:http, so it isn't captured by this patch on current Node versions.

Auto-capture ORM queries

Attach to your ORM / query builder and every executed statement is forwarded as a query dump (SQL + bindings + timing). Each helper uses the library's own public event API — no monkey-patching — and returns an uninstaller.

1const { captureKnex, capturePrisma, captureSequelize } = require('dumpio-client')
2 
3captureKnex(knex) // Knex — via query / query-response / query-error events
4 
5// Prisma — the client must be created with query-event logging:
6const prisma = new PrismaClient({ log: [{ emit: 'event', level: 'query' }] })
7capturePrisma(prisma)
8 
9captureSequelize(sequelize) // Sequelize — wraps the `logging` option (keeps yours)

Each accepts { channel } (default query). capturePrisma can't detach a Prisma listener, so its uninstaller just stops emitting; the others fully detach.

Forward your logger (console / Pino / Winston)

Send your app's log records to the viewer as log dumps (level → flag), each via the logger's own extension point:

1const { captureConsole, pinoStream, winstonTransport } = require('dumpio-client')
2 
3captureConsole() // console.log/info/warn/error/debug → viewer (still logs)
4 
5const logger = pino(pinoStream()) // Pino: a destination stream
6 
7winston.add(winstonTransport()) // Winston: a transport (needs winston-transport)

captureConsole() is idempotent and returns an uninstaller; pinoStream() parses Pino's NDJSON (numeric levels mapped, pid/hostname/time dropped from the detail); winstonTransport() forwards { level, message, ...meta }. All take { channel } (default logs).

dumpio-tail — forward log files

For logs that don't go through the SDK (laravel.log, nginx, php-fpm, …), the package ships a standalone agent that tails a file and forwards each new line to the viewer as a log dump. It handles log rotation, detects the level per line, redacts obvious secrets (passwords / tokens / Bearer …), and rate-limits.

1npx dumpio-tail storage/logs/laravel.log
2npx dumpio-tail --channel nginx --rate 100 /var/log/nginx/error.log

Options: --host / --port / --token (or DUMPIO_* env vars), --channel (default logs), --level (fallback level), --rate (lines/sec, 0 = unlimited), --from-start, --redact <regex> (repeatable), --no-redact. Pairs with the viewer's log mode.

What gets sent (the wire format)

Every dump carries a standard envelope (timestamp, flag, channel, schemaVersion, type, …) plus type-specific fields. The exact envelope, flags, and limits are described in Dump format; the value tree is the same as in the other SDKs.