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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@fetchkit/chaos-fetch

v0.7.2

Published

Client-side chaos-testing library for JavaScript/TypeScript apps.

Downloads

39

Readme

npm version npm downloads GitHub stars CI codecov

chaos-fetch

A TypeScript/ESM client library for injecting network chaos (latency, failures, drops, etc.) into fetch requests. Inspired by chaos-proxy, but designed for programmatic use and composable middleware.

Features

  • Simple configuration via JavaScript/TypeScript
  • Programmatic API for fetch interception
  • Built-in middleware primitives: latency, latencyRange, fail, failRandomly, failNth
  • Extensible registry for custom middleware
  • Route matching by method, domain, and path
  • Built on Koa components (@koa/router and koa-compose), it supports both request and response interception/modification
  • Robust short-circuiting: middleware can halt further processing

Installation

npm install @fetchkit/chaos-fetch

Usage

import { createClient, registerMiddleware } from '@fetchkit/chaos-fetch';

// Register a custom middleware (optional)
registerMiddleware('customDelay', (opts) => async (ctx, next) => {
	await new Promise(res => setTimeout(res, opts.ms));
	await next();
});

const chaosFetch = createClient({
	global: [                                        // Global rules
		{ customDelay: { ms: 50 } },                   // Use custom middleware
		{ failRandomly: { rate: 0.1, status: 503 } },  // 10% random failures
	],
	routes: {
		'GET api.example.com/users/:id': [             // Specific route rules
			{ failNth: { n: 3, status: 500 } },          // Fail every 3rd request with status 500
		],
	},
});

// Use as a drop-in replacement for fetch
const res = await chaosFetch('https://api.example.com/users/123');

// Or replace global fetch
replaceGlobalFetch(chaosFetch);
fetch('https://api.example.com/users/123'); // now goes through chaosFetch
restoreGlobalFetch(); // to restore original fetch

Configuration

  • global: Ordered array of middleware nodes applied to every request
  • routes: Map of method+domain+path to ordered array of middleware nodes
  • Both global and routes are optional. If omitted, no global or route-specific middleware will be applied.
  • Middleware node: { latency: 100 }, { failRandomly: { rate: 0.1, status: 503 } }, etc.

Routing

Chaos Proxy uses @koa/router for path matching, supporting named parameters (e.g., /users/:id), wildcards (e.g., *), and regex routes.

  • Example: "GET /api/*" matches any GET request under /api/.
  • Example: "GET /users/:id" matches GET requests like /users/123.

Supported Route Patterns:

  • Named parameters: /users/:id — Matches any path like /users/123. The value is available in middleware as ctx.params.id.
  • Wildcards: /api/* — Matches any path under /api/. The matched part is available as ctx.params[0].
  • Regex: /files/(.*) — Matches any path under /files/. The matched part is available as ctx.params[0].

In your custom middleware, you can access all matched parameters via the Koa context object (ctx.params). For example, for /users/:id, ctx.params.id will contain the value from the URL. For wildcards and regex, use ctx.params[0] for the first matched segment.

Rule inheritance:

  • Domains are not considered in route matching, only the method and path. This simplification is a tradeoff: it reduces configuration complexity but means you cannot target rules to specific domains. If you need domain-specific behavior, consider using separate clients or custom middleware.
  • There is no inheritance between global and route-specific middleware.
  • Global middlewares apply to every request.
  • Route middlewares only apply to requests matching that route.
  • If a request matches a route, only the middlewares for that route (plus global) are applied. Route rules do not inherit or merge from parent routes or wildcards.
  • If multiple routes match, the most specific one is chosen (e.g., /users/:id over /users/*).
  • If no route matches, only global middlewares are applied.
  • Order of middleware execution: global middlewares run first, followed by route-specific middlewares in the order they are defined. Example: If you have a global latency of 100ms and a route-specific failNth, a request to that route will first incur the 100ms latency, then be subject to the failNth logic.
  • Routes can be defined with or without HTTP methods. If a method is specified (e.g., GET /path), the rule only applies to that method. If no method is specified (e.g., /path), the rule applies to all methods for that path.

Relative URLs:

  • If you use relative URLs (e.g., /api/data), the client will resolve them against globalThis.location.origin in browsers and JSDOM. In Node, Bun, or Deno, you have to provide a full absolute URL; otherwise, they will throw an error.

Middleware Primitives

  • latency(ms) - delay every request with ms
  • latencyRange({ min, max }) - random delay between min and max ms
  • fail({ status, body }) - always fail sending status and body
  • fail({ status, body }) - always send status and body. status defaults to 200, and body defaults to an empty string. Use this to mock responses without making actual network requests.
  • failRandomly({ rate, status, body }) - fail with probability sending status and body
  • failNth({ n, status, body }) - fail every nth request with status and body
  • rateLimit({ limit, windowMs, key }) - rate limit to limit requests per windowMs milliseconds for each unique key (e.g., header, user, IP). Responds with 429 if limit exceeded
  • throttle({ rate, chunkSize, key }) - limit response bandwidth to rate bytes per second, chunking responses by chunkSize bytes, for each unique key (e.g., header, user, IP).

Rate Limiting

The rateLimit middleware restricts how many requests a client can make in a given time window. It uses an internal cache to track requests per key.

  • limit: Maximum number of requests allowed per window (e.g., 100)
  • windowMs: Time window in milliseconds (e.g., 60000 for 1 minute)
  • key: How to identify clients (default is IP, but can be a header name or a custom function)

How it works:

  • Each incoming request is assigned a key (usually the client's IP address).
  • The proxy tracks how many requests each key has made in the current window.
  • If the number of requests exceeds limit, further requests from that key receive a 429 Too Many Requests response until the window resets.
  • You can customize the keying strategy to rate-limit by IP, by a specific header (e.g., Authorization), or by any custom logic.

Throttling

The throttle middleware simulates slow network conditions by limiting the bandwidth of responses. It works by chunking the response body and introducing delays between chunks, based on the configured rate. If streaming is not supported in the runtime, it falls back to delaying the entire response.

  • rate (required): Maximum bandwidth in bytes per second (e.g., 1024 for 1KB/sec).
  • chunkSize (optional): Size of each chunk in bytes (default: 16384).
  • key (optional): Used to throttle per client; can be a header name or a custom function.

How it works:

  • If the response body is a stream (Node.js Stream or browser/edge ReadableStream), the middleware splits it into chunks and delays each chunk to match the specified rate.
  • If the response body is not a stream (e.g., string, buffer), the middleware calculates the total delay needed to simulate the bandwidth and delays the response accordingly.
  • The middleware uses feature detection to choose the best throttling strategy for the current runtime.

Limitations:

  • True stream throttling is only available in runtimes that support streaming APIs (Node.js, browser, edge).
  • In runtimes without streaming support, only total response delay is simulated, not progressive delivery.
  • The accuracy of throttling may vary depending on the runtime and timer precision.
  • Not intended for production use; designed for local development and testing.

Extensibility

Register custom middleware:

registerMiddleware('myMiddleware', (opts) => async (ctx, next) => {
	// custom logic
	await next();
});

Under the hood, chaos-proxy uses Koa, so your custom middleware can leverage the full Koa context and ecosystem. Note that Koa middleware functions are async and take (ctx, next) parameters. Read more in the Koa docs. The reason for switching from Express to Koa is to enable async/await support which helps intercept both requests and responses more easily. In the /examples/middlewares folder, you can find a custom middleware implementation.

Testing

  • Run tests: npm run test:ci
  • Check coverage: npm run test:ci -- --coverage

Security & Limitations

  • Intended for local/dev/test only
  • Not intended for stress testing
  • Does not proxy or forward requests; wraps fetch only

Join the Community

Have questions, want to discuss features, or share examples? Join the Fetch-Kit Discord server:

Discord

License

MIT