New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@apitraffic/utilities

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@apitraffic/utilities

Utilities that are included in all the node framework sdks.

latest
Source
npmnpm
Version
3.0.0
Version published
Maintainers
1
Created
Source

@apitraffic/utilities

Core utilities library that provides shared functionality for all ApiTraffic Node.js framework SDKs. This package contains common utilities for request management, outbound interception, data handling, and configuration management.

Installation

npm install @apitraffic/utilities

Features

  • Request Management - Centralized request tracking and context management
  • Outbound Interception - Automatic http/https and fetch request interception and monitoring
  • Configuration Management - Unified configuration handling with environment variable support
  • Data Processing - Request/response body handling and duration calculation
  • Context Management - Request context and tagging system
  • Settings Management - Dynamic settings retrieval and caching

Quick Start

const { setup, RequestManager, config } = require('@apitraffic/utilities');

// Initialize ApiTraffic utilities
await setup({
  token: 'your-api-token',
  bucket: 'your-bucket-id',
  ingestHost: 'ingest.apitraffic.io',
  apiHost: 'api.apitraffic.io',
  interceptOutbound: true,
  debug: false
});

// Create a request manager
const requestManager = new RequestManager(config);
requestManager.tag('environment', 'production');

API Reference

Setup Function

Initialize the ApiTraffic utilities with your configuration.

const { setup } = require('@apitraffic/utilities');

await setup(options, context);

Options

OptionTypeRequiredDescription
tokenstringYesYour ApiTraffic API token
bucketstringYesYour ApiTraffic bucket ID
ingestHoststringNoIngest endpoint host (default: from env)
apiHoststringNoAPI endpoint host (default: from env)
interceptOutboundbooleanNoEnable outbound request interception (default: true)
captureFetchbooleanNoCapture outbound fetch calls (default: true)
maxFetchBodyLengthnumberNoByte cap per captured fetch request/response body (default: 524288, i.e. 512 KB)
maxBodyBytesnumberNoByte cap per captured request/response body, applied after redaction; longer bodies are truncated with a marker (default: 524288, i.e. 512 KB; env API_TRAFFIC_MAX_BODY_BYTES)
requestSampleRatenumberNoFraction (0–1) of requests sent to ApiTraffic. Server errors (5xx) are always sent (default: 1.0; env API_TRAFFIC_REQUEST_SAMPLE_RATE)
debugbooleanNoEnable debug logging (default: false)

Environment Variables

You can also configure using environment variables:

  • API_TRAFFIC_TOKEN - Your API token
  • API_TRAFFIC_BUCKET - Your bucket ID
  • API_TRAFFIC_INGEST_HOST - Ingest endpoint host
  • API_TRAFFIC_API_HOST - API endpoint host
  • API_TRAFFIC_INTERCEPT_OUTBOUND - Enable outbound interception
  • API_TRAFFIC_CAPTURE_FETCH - Capture outbound fetch calls
  • API_TRAFFIC_MAX_FETCH_BODY_LENGTH - Byte cap per captured fetch body (default: 524288)
  • API_TRAFFIC_REDACT_SENSITIVE_KEYS - Mask credential-shaped body keys and query parameters by default (default: true)
  • API_TRAFFIC_INGEST_BATCH - Post captured requests to ingest in batches when your account and the ingest host offer it (default: true)
  • API_TRAFFIC_INGEST_GZIP - Gzip those batches when your account asks for it (default: true)
  • API_TRAFFIC_DEBUG - Enable debug mode

A gzipped batch compresses several captured payloads (already redacted) in one stream, so how well it compresses depends on what those payloads have in common. Where an attacker could both inject chosen content into captured traffic and observe the encrypted sizes of the SDK's outbound requests to ingest, set ingestGzip: false (API_TRAFFIC_INGEST_GZIP=false), or ask for the account's ingest.batch.gzip flag to be turned off, to send batches uncompressed.

Built-in redaction

Before anything leaves the process, values under credential-shaped names are replaced with [*** API REDACTED ***], whatever the account's own rules say — in JSON and application/x-www-form-urlencoded bodies and in the captured URL's query string (inbound and outbound). The rules aim at names that hold a secret, not names that mention one:

  • password, passwd, passcode, secret, apikey, accesskey, privatekey, credential, signature, assertion, jwt anywhere in the name (separators ignored: api_key, x-api-key and apiKey all match);
  • names ending in token (access_token, csrfToken), but not pagination cursors (pageToken, nextToken) or counts (max_tokens);
  • the words auth, pass, pwd, otp, sig, mfa, totp; secret_key-style keys and auth_code-style codes; session, sessionid, sid, authorization, cookie;
  • in the query string only: key, code and the word pin.

Booleans, null and empty values are kept. Bodies are edited in place — only the masked values change, so nothing else about a captured body is altered. Multipart and XML bodies are not key-masked. Credential headers (authorization, cookie, x-api-key, …) are always redacted.

Turn the key masking off with redactSensitiveKeys: false (or the env var above). The SDK also honours an account-level redaction.sensitiveKeys: false in its settings, for when the dashboard offers that switch. Account rules scoped to request.query apply to the captured URL's query string, and key rules apply to form bodies.

Outbound fetch capture

Outbound http/https calls (axios, got, node-fetch’s http path, …) are captured by patching http.request. Global fetch never goes through it — it drives Node’s built-in undici straight onto a socket — so it has its own hook, installed with the rest of the outbound interception. A fetch call is reported with the same detail as an axios one: URL, method, the headers actually put on the wire (taken from undici’s sendHeaders diagnostic event, so host, content-length and accept-encoding are included), request and response bodies, status, response headers and timing, plus an inline http.out span on the request that made the call.

Capture never alters the call: the caller gets back the exact Response object, and bodies are read from clone()s. Two consequences worth knowing:

  • Payloads are capped at maxFetchBodyLength, 512 KB by default (the http path's cap is 3 MB), because reading a copy of a response tees the stream — a higher cap holds a second copy of a large download in memory alongside your own. Bodies past the cap are stored truncated and flagged. Raise it to whatever your payloads and headroom justify:

    await setup({ /* … */ maxFetchBodyLength: 512 * 1024 * 1024 }); // 512 MB
    
    API_TRAFFIC_MAX_FETCH_BODY_LENGTH=536870912
    
  • A request body that is a stream, FormData or Blob, and non-textual or text/event-stream responses, are reported as metadata only (bodyCaptured: false) rather than being consumed or rewritten.

Set captureFetch: false (or API_TRAFFIC_CAPTURE_FETCH=false) to leave globalThis.fetch untouched.

The hook itself needs nothing newer than Node 18 (verified on 18.20 and 20.18). On a runtime without a global fetch the hook simply doesn't install, and if the undici wire-header event is unavailable the headers the caller set are reported instead.

Two properties worth knowing about the hook itself:

  • The hook delegates to whatever globalThis.fetch was at install time and marks itself, so installing twice (or from a duplicated copy of this package) does not stack wrappers, and another library wrapping fetch afterwards still runs above it. Code that replaces globalThis.fetch outright after setup (rather than wrapping it) removes the hook, and those calls go uncaptured.
  • Wire headers are attributed per call by undici request identity (via undici:request:create in the caller's async context), not by URL, so concurrent identical requests — including ones dispatched out of order or queued on a pooled connection — cannot swap header sets.

RequestManager

Manages request context, tags, and traces for individual requests.

const { RequestManager } = require('@apitraffic/utilities');

const manager = new RequestManager(settings);

Methods

tag(key, value) Add a tag to the current request context.

manager.tag('user_id', '12345');
manager.tag('environment', 'production');

removeTag(key) Remove a tag from the current request context.

manager.removeTag('user_id');

trace(message) Add a trace message to the request.

manager.trace('Database query executed');

Utility Functions

getDuration Calculate duration between two time points.

const { getDuration } = require('@apitraffic/utilities');

const startTime = process.hrtime();
// ... some operation
const duration = getDuration(startTime);

handleResponseBody Process and handle response body data.

const { handleResponseBody } = require('@apitraffic/utilities');

const processedBody = handleResponseBody(responseData, options);

sendToApiTraffic Send request data to ApiTraffic service.

const { sendToApiTraffic } = require('@apitraffic/utilities');

await sendToApiTraffic(options, payload);

Span fingerprint helpers

This package is the single source of truth for how ApiTraffic turns a raw operation into a stable fingerprint (so identical SQL/Redis/HTTP/custom operations collapse to one identity for GROUP BY fingerprint analytics). The @apitraffic/apm OTel bridge imports these same helpers, so a manually-timed span and an OTel-captured span for the same operation get an identical fingerprint. Inputs are raw — normalization/masking happens inside.

const {
  normalizeSql,       // "SELECT * FROM t WHERE id = 42" -> "SELECT * FROM t WHERE id = ?"
  maskRedisKey,       // "user:12345:profile" -> "user:{}:profile"
  fingerprintSql,     // (statement) -> "fpr_<16hex>"
  fingerprintRedis,   // (type, command, key, patterns?) -> "fpr_<16hex>"
  fingerprintHttpOut, // (method, host, pathPattern) -> "fpr_<16hex>"
  fingerprintGeneric, // (type, name) -> "fpr_<16hex>"
  fingerprintFrom,    // (parts:string) -> "fpr_<16hex>"  (low-level)
} = require('@apitraffic/utilities');

maskRedisKey and the *Redis* helpers accept an optional redisKeyPatterns: Array<string | RegExp> to override the built-in variable-segment heuristic for key shapes it can't infer on its own.

Configuration

Access the global configuration object:

const { config } = require('@apitraffic/utilities');

console.log(config.apiToken);
console.log(config.bucketSid);
console.log(config.ingestHost);

Context Management

Manage request context across your application:

const { context } = require('@apitraffic/utilities');

// Get current context
const currentContext = context.get();

// Set context data
context.set('requestId', 'req-123');

Requirements

  • Node.js 20.0.0 or higher
  • Valid ApiTraffic account and API token

Dependencies

This package includes the following dependencies:

  • @apitraffic/data-redaction - Data redaction utilities
  • @apitraffic/javascript-helpers - JavaScript helper functions
  • @faker-js/faker - Data generation utilities
  • ably - Real-time messaging
  • axios - HTTP client
  • ksuid - Unique ID generation
  • pako - Data compression
  • systeminformation - System information gathering

What is captured

The framework packages report each inbound request as the client received it. The request and the response are captured when the response is committed — when its first bytes are handed to the socket (onCommit) — not when it finishes, which for a large response is only once the client has read it. The capture runs after those bytes are written, so it never delays the response, and a write that throws (an invalid header value or status code) commits nothing: the error response that follows is what is reported.

Request body means the same on every framework: the parsed body as the application has it when it responds — including changes its middleware, hooks or handler made before responding, and none made afterwards. Usually that is the commit itself. Where a layer holds the bytes back past the application's call — compression or express-session mounted after the SDK, an async Fastify onSend hook, Hapi's gzip — the request alone is snapshotted when the application starts responding (onCommit's start), the one capture step that runs ahead of the response's bytes and only on that path. It is kept cheap: a parsed body whose request declared more than maxRedactBodyBytes (Content-Length) is replaced by the placeholder without being serialized, and any other parsed body is serialized by the native serializer and settled into exactly what the bounded serializer makes of it (nesting cap, size limit, Buffers) when the payload is prepared, off the response path.

PackageRequest bodyNot seen
@apitraffic/expressreq.bodychanges after res.send() / res.end()
@apitraffic/fastifyrequest.bodychanges after reply.send() (and in onResponse hooks)
@apitraffic/koactx.request.bodychanges after the response is written
@apitraffic/hapirequest.payloadchanges once the response is transmitted (and in onPostResponse extensions)

Response body is what the client was sent: on Fastify, the payload handed to reply.raw — after the application's own onSend hooks (one that scrubs a field for the client) — or, for a stream or a content-encoded body, the payload as it reached the SDK's onSend. A content-encoded (gzip) body is never reported as its compressed bytes, on any framework.

Status and timing are recorded when the response finishes (499 for a client that disconnected first). A body nested more than 256 levels deep — parsed, or JSON text the framework left as a string, when account redaction rules would parse it — or one that cannot be read or redacted is sent as a [body not captured: …] placeholder and counted in the bodyNotCaptured delivery stat. A request whose redaction cannot be completed even without its bodies is never sent: it is dropped and counted as droppedUnredactable.

Delivery and proxies

Each captured request is sent to ingest as one JSON POST, prepared and sent off the response path (on setImmediate, after the response has been written), on the SDK's own keep-alive agents, with bounded concurrency, a small retry queue and a circuit breaker. Ingest posts use Node's http/https directly. When HTTP_PROXY / HTTPS_PROXY / ALL_PROXY (or their npm_config_* forms) apply to the ingest URL's protocol, the posts go through axios instead, as before, so its proxy support (NO_PROXY, CONNECT tunnelling for HTTPS) still applies. The proxy variables are re-read at most every 10 seconds. The SDK's own calls (ingest, settings, host telemetry, embed tokens) are never captured as outbound calls of the application.

Error Handling

The utilities include comprehensive error handling:

try {
  await setup({
    token: 'invalid-token',
    bucket: 'invalid-bucket'
  });
} catch (error) {
  console.error('Setup failed:', error.message);
}

Common errors:

  • Missing API token or bucket ID
  • Invalid Node.js version (< 18.0.0)
  • Network connectivity issues
  • Invalid configuration parameters

Debug Mode

Enable debug mode to see detailed logging:

await setup({
  token: 'your-token',
  bucket: 'your-bucket',
  debug: true
});

Or via environment variable:

export API_TRAFFIC_DEBUG=true

License

MIT License - see LICENSE file for details.

Support

For support and documentation, visit ApiTraffic.io or contact sdk@apitraffic.io.

FAQs

Package last updated on 03 Nov 2026

Related posts