npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nklient

v2.0.0

Published

Secure, observable HTTP client for Node.js with schema-validated config, retry logic, and fluent API

Downloads

10

Readme

nklient

A modern, feature-rich HTTP request client for Node.js with support for HTTPS, cookies, retry logic, interceptors, and more.

CI Coverage npm version License

Features

  • Promise-based API with async/await support
  • HTTPS support with certificate validation options
  • Cookie management with automatic cookie jar
  • Retry logic with exponential backoff
  • Request/Response interceptors for middleware functionality
  • Streaming support for large files
  • Proxy support for HTTP/HTTPS proxies
  • Compression handling (gzip, deflate, brotli)
  • Redirect following with method change support
  • Query string and form data helpers
  • Timeout support with configurable delays
  • Custom agents for connection pooling
  • TypeScript definitions included
  • Small dependency footprint (only http/https-proxy-agent and tough-cookie at runtime — config validation, LRU caches and multipart encoding are zero-dependency internals; see package.json)

Installation

npm install nklient

Full documentation (guides, guard reference, error catalog, configuration reference) is published at https://nitishagar.github.io/nklient/ (source in website/).

Quick Start

const nklient = require('nklient');

// Simple GET request
const response = await nklient.get('https://api.example.com/users').exec();
console.log(response.body);

// POST request with JSON
const newUser = await nklient.post('https://api.example.com/users')
  .json({ name: 'John Doe', email: '[email protected]' })
  .exec();

// Using async/await
try {
  const data = await nklient.get('https://api.example.com/data')
    .headers('Authorization', 'Bearer token')
    .query({ page: 1, limit: 10 })
    .exec();
  console.log(data.body);
} catch (error) {
  console.error('Request failed:', error.message);
}

API Reference

HTTP Methods

All HTTP methods return a RequestWrapper instance that can be configured with chainable methods.

nklient.get(url)
nklient.post(url)
nklient.put(url)
nklient.patch(url)
nklient.delete(url)
nklient.head(url)
nklient.options(url)

Request Configuration

Headers

// Set individual header
nklient.get(url)
  .headers('Authorization', 'Bearer token')
  .headers('X-Custom', 'value')

// Set multiple headers
nklient.get(url)
  .headers({
    'Authorization': 'Bearer token',
    'X-Custom': 'value'
  })

Request Body

// JSON body (auto-sets Content-Type)
nklient.post(url).json({ key: 'value' })

// Form data
nklient.post(url).form({ username: 'john', password: 'secret' })

// Raw body
nklient.post(url).body('raw string data')
nklient.post(url).body(Buffer.from('binary data'))

Query Parameters

nklient.get(url).query({ page: 1, limit: 10 })
// Results in: url?page=1&limit=10

Timeout

nklient.get(url).timeout(5000) // 5 seconds

Cookies

// Use custom cookie jar
const jar = nklient.jar();
nklient.get(url).jar(jar)

// Disable cookies for a request
nklient.get(url).noJar()

Retry Configuration

nklient.get(url).retry({
  attempts: 3,
  delay: 1000,
  maxDelay: 10000,
  backoffMultiplier: 2,
  retryOnStatusCodes: [408, 429, 500, 502, 503, 504]
})

Other Options

nklient.get(url)
  .maxRedirects(5)           // Maximum number of redirects
  .encoding('utf8')          // Response encoding (null for Buffer)
  .stream()                  // Get response as stream
  .rejectUnauthorized(false) // Disable SSL certificate validation
  .proxy('http://proxy.example.com:8080') // Use proxy
  .agent(customAgent)        // Use custom HTTP agent

Response Object

{
  statusCode: 200,
  headers: {
    'content-type': 'application/json',
    // ... other headers
  },
  body: { /* parsed JSON or string/Buffer */ },
  request: {
    uri: 'https://example.com/api',
    method: 'GET',
    headers: { /* request headers */ }
  }
}

Interceptors

Add middleware to requests and responses:

// Request interceptor
const requestId = nklient.interceptors.request.use(async (config) => {
  config.headers['X-Request-ID'] = generateId();
  return config;
});

// Response interceptor
const responseId = nklient.interceptors.response.use(async (response) => {
  console.log(`Response: ${response.statusCode}`);
  return response;
});

// Remove interceptor
nklient.interceptors.request.eject(requestId);
nklient.interceptors.response.eject(responseId);

Custom Instances

Create instances with custom defaults:

const api = nklient.create({
  headers: {
    'Authorization': 'Bearer token',
    'Content-Type': 'application/json'
  },
  timeout: 10000,
  retry: {
    attempts: 5,
    delay: 1000
  }
});

// Use instance
const response = await api.get('/users').exec();

Global Configuration

No global nklient.defaults() API exists — use create() / createClient() with per-instance config (see Custom Instances above).

Advanced Examples

File Upload with Streaming

const fs = require('fs');

const response = await nklient.post('https://api.example.com/upload')
  .headers('Content-Type', 'application/octet-stream')
  .body(fs.createReadStream('large-file.zip'))
  .exec();

Download File with Progress

const fs = require('fs');

const response = await nklient.get('https://example.com/large-file.zip')
  .stream()
  .exec();

const fileStream = fs.createWriteStream('downloaded-file.zip');
let downloaded = 0;

response.body.on('data', (chunk) => {
  downloaded += chunk.length;
  console.log(`Downloaded: ${downloaded} bytes`);
});

response.body.pipe(fileStream);

Error Handling with Retry

try {
  const response = await nklient.get('https://flaky-api.example.com/data')
    .retry({
      attempts: 3,
      delay: 1000,
      backoffMultiplier: 2,
      retryOnStatusCodes: [408, 429, 500, 502, 503, 504]
    })
    .timeout(5000)
    .exec();
    
  console.log('Success:', response.body);
} catch (error) {
  if (error.code === 'ETIMEDOUT') {
    console.error('Request timed out');
  } else if (error.code === 'ECONNREFUSED') {
    console.error('Connection refused');
  } else {
    console.error('Request failed:', error.message);
  }
}

Using with Proxy

const response = await nklient.get('https://api.example.com/data')
  .proxy('http://proxy.company.com:8080')
  .exec();

Cookie Management

const jar = nklient.jar();

// Login request - cookies are saved
await nklient.post('https://api.example.com/login')
  .jar(jar)
  .json({ username: 'user', password: 'pass' })
  .exec();

// Subsequent requests use saved cookies
const profile = await nklient.get('https://api.example.com/profile')
  .jar(jar)
  .exec();

Development

# Install dependencies
npm install

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Run tests in watch mode
npm run test:watch

# Lint code
npm run lint

# Format code
npm run format

Development Status

Features Implemented

All core features are implemented and working:

  • ✅ Retry Logic - Exponential backoff with configurable retry conditions
  • ✅ Cookie Handling - Automatic cookie management with tough-cookie
  • ✅ Request Cancellation - AbortController support for request cancellation
  • ✅ Streaming Support - Request and response streaming with progress tracking
  • ✅ Proxy Support - HTTP/HTTPS proxy support with proper agent handling

Test Coverage

Current Status: 224 passing, 0 pending, 0 failing — 15 end-to-end tests against live local servers (tests/e2e.test.js, zero mocks) plus the full mocked suite.

Coverage (c8 on dist/): see thoughts/shared/plans/nklient-full-feature-set/TEST_VALIDATION_S4.md for the final gate table (gates: 90 lines / 85 funcs / 80 branches).

S1 — Typed errors

Redirect-limit and oversize-body rejections throw typed errors (assert err.code, not message text):

  • MaxRedirectsError (code: 'ERR_MAX_REDIRECTS') on redirect-limit breach.
  • ResponseTooLargeError (code: 'ERR_RESPONSE_TOO_LARGE') on maxResponseSize breach (stream destroyed mid-body).

S2 — Boundaries + abort-during-backoff

Negative numeric inputs throw RangeError at the setter seam (mirrors createClient schema minima of 0); zero has pinned per-field meaning:

| Input | 0 | Negative | |---|---|---| | timeout (scalar / object value) | no timeout (disabled) | throw RangeError | | maxRedirects | no follows (first redirect rejects MaxRedirectsError) | throw RangeError | | maxResponseSize | no limit (same as unset) | throw RangeError | | retry.attempts | 0/1 = single attempt (no RetryExhaustedError) | throw RangeError | | retry.delay | no delay (immediate retry) | throw RangeError |

Abort wins during retry backoff: aborting mid-backoff rejects AbortError (ERR_ABORTED) promptly instead of sleeping through the full delay (no further attempt fires).

S3 — Redirect-security API

Secure by default; guards apply to redirect targets only (same-hostname hops exempt from the private-network guard):

  • allowHttpsToHttp (default false) — blocks https: → http: downgrades unless opted in (ProtocolDowngradeError, PROTOCOL_DOWNGRADE).
  • blockPrivateNetworks (default true) — blocks cross-host pivots into private address space unless opted out (PrivateNetworkError, PRIVATE_NETWORK).
  • allowedDomains / blockedDomains (default [] = no filtering) — subdomain-aware allowlist/blocklist; blocklist wins (UnauthorizedDomainError, UNAUTHORIZED_DOMAIN / BlockedDomainError, BLOCKED_DOMAIN).
  • Redirect cycles fail fast (RedirectLoopError, REDIRECT_LOOP).
  • TLS 1.2 minimum by default on HTTPS (minVersion: 'TLSv1.2' on the global agent and per request); rejectUnauthorized(false) only warns (never throws).
  • Responses carry a data alias (same reference as body).

Passing Test Areas:

  • Basic HTTP methods (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
  • HTTPS support and certificate validation
  • Headers, query parameters, and request body handling
  • Cookie management and retry logic
  • Interceptors and streaming support
  • Custom agents and createClient functionality

License

Apache License 2.0