NestJS Profiler
API Reference

nest-profiler-http

Types and API of the HTTP Client panel and its axios/fetch adapters.

This package owns the HTTP Client panel: the HttpRequestEntry contract, the collector, the injectable HttpProfilerRecorder and the HttpInstrumentation interface. It ships two opt-in adapters — AxiosInstrumentation (/axios) and FetchInstrumentation (/fetch) — each on its own subpath, both capturing request and response bodies safely, plus two opt-in phases providers (/phases) that break a call down into DNS, handshake, time-to-first-byte and download. You select which client(s) to instrument; nothing is patched unless listed. Any other client (undici, got, a custom module…) can feed the same panel through the recorder or a custom HttpInstrumentation.

Module

HttpCollectorModule

Registers the HTTP Client panel and the HttpProfilerRecorder, then installs the HTTP instrumentations you selected in instrumentations. Import it once with HttpCollectorModule.forRoot(options).

HttpCollectorModuleOptions

Options passed to HttpCollectorModule.forRoot(). They extend the shared HttpCaptureOptions capture/redaction contract with an instrumentations array.

Prop

Type

  • instrumentations (default []) - the adapters to install, imported from their subpaths (AxiosInstrumentation, FetchInstrumentation) or your own HttpInstrumentation. Nothing is instrumented unless it appears here.

Gate the module with ConditionalModule.registerWhen(HttpCollectorModule.forRoot({ … }), isProfilerEnabled) so it loads only when the profiler is on — the same pattern used for every profiler module.

Recorder

HttpProfilerRecorder

Injectable façade for recording outgoing HTTP requests into the active profile. This is the API to reach for from application code or a custom instrumentation: inject it and call capture with the raw request/response material. Its maskHeaders property exposes the built-in mask list merged with the configured maskHeaders.

Prop

Type

capture(input: HttpCaptureInput): void builds an HttpRequestEntry from raw request/response material, honouring the configured HttpCaptureOptions (capture flags) and masking sensitive headers, then records it. This is the recommended entry point - it guarantees a custom client captures the same request/response detail (shown in the panel) as the bundled adapters.

record(entry: HttpRequestEntry): void appends an already-built entry as-is (no capture flags / masking applied) - use it only to bypass the options. Both methods are a no-op outside a CLS context or when no profile is active.

HttpCaptureInput

Raw request/response material handed to capture. Header bags may be plain records, fetch Headers, a Map, or axios AxiosHeaders.

Prop

Type

Instrumentations

HttpInstrumentation

Interface to implement when teaching the profiler how to capture requests from a given client. Register implementations via HttpCollectorModule.forRoot({ instrumentations: [MyInstrumentation] }). Implementations are NestJS providers, so they may inject ModuleRef, config, etc. through their constructor.

Prop

Type

install(recorder: HttpProfilerRecorder): void | Promise<void> is called once at application bootstrap. Use recorder.capture(...) to push captured requests - it applies the capture options and header masking for you.

This is the extension point for any client without a bundled adapter (got, undici, superagent, a bespoke NestJS service…): hook the client's own API in install so you capture full request and response bodies safely. For a one-off call, inject HttpProfilerRecorder and call capture(...) inline instead. The HTTP client tutorial walks through both patterns with a complete got example.

Each bundled adapter is exported from its own subpath so importing one never loads another's dependency.

AxiosInstrumentation

import { AxiosInstrumentation } from '@eleven-labs/nest-profiler-http/axios';

The bundled axios adapter. It auto-discovers every axios instance in the DI container via DiscoveryService@nestjs/axios HttpService (including each per-feature HttpModule / HttpModule.register(), which build distinct instances) and bare axios instances — and patches their interceptors. No axiosRef wiring and no @nestjs/axios import; only axios's types are referenced. Instances created outside DI aren't discoverable — record those with a custom instrumentation. Optional peer: axios.

FetchInstrumentation

import { FetchInstrumentation } from '@eleven-labs/nest-profiler-http/fetch';

Patches globalThis.fetch once (Node ≥ 22 built-in). A single global hook captures every caller. Response bodies are read (via Response.clone()) only when captureResponseBody is enabled; request bodies are captured for serialisable init.body (string / URLSearchParams). No dependency.

Phases providers

A phases provider is an HttpInstrumentation that records nothing: it measures where a call spent its time and leaves the breakdown for the adapter that records it. Both are opt-in, listed in instrumentations like any adapter, and exported from the /phases subpath.

import { NodeHttpPhases, UndiciPhases } from '@eleven-labs/nest-profiler-http/phases';

HttpCollectorModule.forRoot({
  instrumentations: [AxiosInstrumentation, NodeHttpPhases, FetchInstrumentation, UndiciPhases],
});

NodeHttpPhases

Wraps request/get on node:http and node:https to start a timer on every outgoing request, covering every client built on them — axios, superagent, got, node-fetch, a hand-rolled https.request. Reports wait, dns, tcp, tls, request, firstByte and download.

This is the timings-only counterpart of the node:http recording adapter this package deliberately does not ship. The objection to that adapter does not apply: capturing a response body means reading the stream, which steals chunks from a caller consuming it in paused mode, whereas a timer only notes when events fired and leaves both streams untouched. It cannot double-record either, because it records nothing.

Not covered: a request built with new http.ClientRequest(...), and any client holding a reference to http.request captured before bootstrap. Time those with instrumentClientRequest.

UndiciPhases

Subscribes to undici's diagnostics_channel events, which is the only way to time fetch: Node's built-in fetch runs on undici and never goes through node:http. Reports wait, connect, request, firstByte and download.

Correlation with the recorded call is exact rather than heuristic — the channel subscribers run in the async context of the fetch() that triggered them, so they find that call's phase slot. Two limits are inherent to what undici reports, and both degrade visibly rather than silently:

  • the handshake is not broken down — undici publishes one connected event covering DNS, TCP and TLS, reported as the coarse connect phase. A request on a pooled connection reports none, which is correct: it connected nothing.
  • download is often absentfetch() resolves as soon as the response headers arrive, which is when the adapter records the call. A body still streaming then has no measured download phase.

Types

HttpRequestEntry

One entry per outgoing HTTP request, surfaced in the HTTP Client panel. Client-agnostic by design: the bundled adapters (axios, fetch) produce these, but any client can record the same shape via HttpProfilerRecorder or appendHttpRequestEntry.

Prop

Type

HttpPhases

Where an outgoing call spent its time, as a breakdown of durations in milliseconds. Durations rather than absolute marks, because that is the shape a client can actually produce: node:http exposes socket events, undici publishes channel events, a bespoke transport may know nothing but its own time-to-first-byte — all three can fill a subset of these fields, whereas none of them share a time base.

Every field is optional, and the phases are not required to add up to the call's duration: the difference is client-side time (interceptors, serialisation, queueing the client never reported) and the panel draws it as an explicit remainder rather than inflating a phase to make the arithmetic tidy. The names are the de-facto vocabulary — got/@szmarczak/http-timer use them, and they map onto the browser's PerformanceResourceTiming.

Prop

Type

HttpCaptureOptions

Capture/redaction flags shared by every instrumentation so they expose the same option surface. HttpCollectorModuleOptions extends it.

Prop

Type

Helpers

appendHttpRequestEntry

function appendHttpRequestEntry(cls: ClsService, entry: HttpRequestEntry): void;

Low-level primitive for feeding the HTTP Client panel directly from a ClsService. Reads the active profile from the CLS store and appends the entry; a no-op outside a CLS context or when no profile is active. In application code, prefer the injectable HttpProfilerRecorder - it wraps this and exposes the merged maskHeaders.

Redaction helpers

Client-agnostic header redaction helpers shared by instrumentations.

  • DEFAULT_MASK_HEADERS: string[] - built-in list of header names masked by default (authorization, cookie, set-cookie, x-api-key, x-auth-token, proxy-authorization).
  • extractHeaders(headers: unknown, maskHeaders: Iterable<string>, options?: ExtractHeadersOptions): Record<string, string> - normalises a header bag (a fetch Headers, an axios AxiosHeaders or a plain record) into a flat record, replacing the values of maskHeaders (compared case-insensitively) with [REDACTED]. Pass recorder.maskHeaders to honour the configured mask list. options.replacement writes a different sentinel; options.multiValue keeps a repeated header as an array instead of joining its values.
  • formatHeaderValue(value: unknown): string - renders an arbitrary header value as a display string.

Phase helpers

Everything needed to feed phases from a client with no bundled provider.

  • readHttpPhases(source: unknown): HttpPhases | undefined - finds the breakdown behind whatever object an instrumentation holds: an AxiosResponse, an axios error, a ClientRequest, an IncomingMessage, a follow-redirects wrapper (the final hop wins — its phases describe the response the caller got), or a got response, whose native timings are read without this package depending on @szmarczak/http-timer. Returns undefined when nothing timed the call, which is the normal state with no provider installed.
  • instrumentClientRequest(request: ClientRequest): void - times one node:http request by listening to the events it already emits, with no global patch. Call it on a freshly created request (the socket event is deferred to the next tick, so instrumenting right after http.request(...) is always in time); idempotent. Useful when a client hands you its request: got.stream(url).on('request', (req) => instrumentClientRequest(req)).
  • phasesOfClientRequest(request: ClientRequest): HttpPhases | undefined - the breakdown measured for an instrumented request.
  • openPhaseSlot<T>(execute: (slot: HttpPhaseSlot) => T): T / activePhaseSlot(): HttpPhaseSlot | undefined / phaseSlotsEnabled(): boolean / registerPhaseSlotProvider(): void - the async-context channel a provider and an adapter use when the client never exposes its transport (the fetch + undici case). An adapter opens a slot around each call only while phaseSlotsEnabled(), so with no provider installed no async context is entered at all.
  • sumHttpPhases(phases), hasHttpPhases(phases), formatPhaseDuration(ms), HTTP_PHASE_SEQUENCE, HTTP_PHASE_LABELS, HTTP_PHASE_HINTS - the display vocabulary, shared by the panel and the trace so a phase is named and explained the same way everywhere.

Constants

  • HTTP_CLIENT_REQUESTS_KEY - the private profile.collectors key where instrumentations accumulate raw HttpRequestEntry items before the collector migrates them to the public http-client key.
  • HTTP_COLLECTOR_OPTIONS - DI token for the resolved HttpCollectorModuleOptions, injected by HttpProfilerRecorder.
  • HTTP_INSTRUMENTATIONS - DI token for the array of resolved HttpInstrumentation instances.

Public exports

The root barrel is client-agnostic — it exports no adapter. Select adapters from their subpaths.

import {
  HttpCollectorModule,
  HttpProfilerRecorder,
  HttpClientCollector,
  appendHttpRequestEntry,
  DEFAULT_MASK_HEADERS,
  extractHeaders,
  formatHeaderValue,
  HTTP_CLIENT_REQUESTS_KEY,
  HTTP_COLLECTOR_OPTIONS,
  HTTP_INSTRUMENTATIONS,
  readHttpPhases,
  instrumentClientRequest,
  phasesOfClientRequest,
  openPhaseSlot,
  activePhaseSlot,
  phaseSlotsEnabled,
  registerPhaseSlotProvider,
  sumHttpPhases,
  hasHttpPhases,
  formatPhaseDuration,
  redactQueryString,
  resolveMaskedQueryParams,
  DEFAULT_MASK_QUERY_PARAMS,
  HTTP_PHASE_SEQUENCE,
  HTTP_PHASE_LABELS,
  HTTP_PHASE_HINTS,
} from '@eleven-labs/nest-profiler-http';

import type {
  HttpCollectorModuleOptions,
  HttpCollectorModuleAsyncOptions,
  HttpInstrumentation,
  HttpRequestEntry,
  HttpCaptureInput,
  HttpCaptureOptions,
  HttpPhases,
  HttpPhaseName,
  HttpPhaseSlot,
} from '@eleven-labs/nest-profiler-http';

// Adapters — one per subpath:
import { AxiosInstrumentation } from '@eleven-labs/nest-profiler-http/axios';
import { FetchInstrumentation } from '@eleven-labs/nest-profiler-http/fetch';

// Phases providers — same subpath, both optional:
import { NodeHttpPhases, UndiciPhases } from '@eleven-labs/nest-profiler-http/phases';

Installation

pnpm add @eleven-labs/nest-profiler-http
# only if you select the axios adapter — your app already owns these:
pnpm add @nestjs/axios axios

Optional peer dependency: axios ^1.0.0 (type-only, used by the /axios adapter). fetch is a Node ≥ 22 built-in and needs no dependency. This package never imports @nestjs/axios — that is your application's dependency.

Setup

Select the client(s) to instrument via instrumentations, importing each adapter from its subpath:

import { ConditionalModule } from '@nestjs/config';
import { HttpCollectorModule } from '@eleven-labs/nest-profiler-http';
import { AxiosInstrumentation } from '@eleven-labs/nest-profiler-http/axios';
import { FetchInstrumentation } from '@eleven-labs/nest-profiler-http/fetch';

const isProfilerEnabled = (env: NodeJS.ProcessEnv) => env['PROFILER_ENABLED'] === 'true';

@Module({
  imports: [
    ConditionalModule.registerWhen(
      HttpCollectorModule.forRoot({
        instrumentations: [AxiosInstrumentation, FetchInstrumentation],
      }),
      isProfilerEnabled,
    ),
  ],
})
export class PostsModule {}

The axios adapter auto-discovers your HttpService instances, so injecting HttpService as usual is enough — no axiosRef wiring. At bootstrap each selected instrumentation installs its hooks and pushes every HttpRequestEntry to the current profile via the HttpProfilerRecorder. The collector registered by HttpCollectorModule renders the panel.

Powered & maintained by

On this page