Toxiproxy Node Client
The `toxiproxy-node-client` is a JavaScript and TypeScript client library for interacting with Toxiproxy, a powerful tool developed by Shopify for simulating real-world network conditions such as high latency, bandwidth limitations, and connection drops. This library, currently at stable version 4.0.0, provides a programmatic interface to configure Toxiproxy proxies and add various network 'toxics' to test the resilience and error handling of applications. While there isn't a fixed public release cadence, major versions like v4 typically introduce significant API updates or broader compatibility. Its key differentiator is being the primary Node.js interface for Toxiproxy, enabling developers to integrate network chaos engineering directly into their Node.js testing and development workflows without manual intervention. It ships with full TypeScript type definitions, making it suitable for both JavaScript and TypeScript projects.
Common errors
-
connect ECONNREFUSED 127.0.0.1:8474
cause The Toxiproxy server is not running or is inaccessible at the specified address and port.fixStart the Toxiproxy server, for example, using Docker: `docker run --rm -p 8474:8474 ghcr.io/shopify/toxiproxy:latest`. Verify that the `Toxiproxy` client is configured to connect to the correct endpoint. -
Error: Proxy 'my-proxy-name' already exists
cause You attempted to create a proxy with a name that is already in use by another proxy on the Toxiproxy server.fixUse a unique name for each proxy. In testing scenarios, consider programmatically generating unique names (e.g., `my-proxy-${Date.now()}`). -
Argument of type '{ attributes: { rate: number; }; type: "bandwidth"; }' is not assignable to parameter of type 'ICreateToxicBody<T>'.cause When using TypeScript, the generic type `T` for `ICreateToxicBody` (representing the specific toxic's attributes) has not been explicitly provided or inferred correctly.fixExplicitly specify the toxic type, for example, `const toxicBody: ICreateToxicBody<Bandwidth> = { ... };`. Ensure you have imported the specific toxic type (e.g., `Bandwidth`) if needed.
Warnings
- breaking Module Import Syntax: While v4.x supports both CommonJS `require` and ES Module `import` syntax, ensure you use named imports for ESM and TypeScript (e.g., `import { Toxiproxy } from 'toxiproxy-node-client'`). Attempting a default import will result in errors. Earlier versions (e.g., v3.x) might have had different import patterns or primary support for CommonJS.
- gotcha `toxiproxy-node-client` is a client library and requires a running Toxiproxy server instance to function. All operations will fail with connection refused errors (e.g., `ECONNREFUSED`) if the server (typically listening on `http://localhost:8474`) is not accessible.
- gotcha Each proxy created via the client must have a globally unique `name` within the Toxiproxy server instance. Attempting to create a proxy with a name that already exists on the server will result in an error. This is crucial in test suites where proxies might be created and torn down frequently.
- gotcha Similarly, each toxic added to a specific proxy must have a unique `name` within that proxy. Using duplicate toxic names for the same proxy will cause errors from the Toxiproxy server.
Install
-
npm install toxiproxy-node-client -
yarn add toxiproxy-node-client -
pnpm add toxiproxy-node-client
Imports
- Toxiproxy
import Toxiproxy from 'toxiproxy-node-client';
import { Toxiproxy } from 'toxiproxy-node-client'; - ICreateProxyBody
const ICreateProxyBody = require('toxiproxy-node-client').ICreateProxyBody;import type { ICreateProxyBody } from 'toxiproxy-node-client'; - Bandwidth
import type { Bandwidth } from 'toxiproxy-node-client';
Quickstart
import { Toxiproxy, ICreateProxyBody, ICreateToxicBody, Bandwidth } from "toxiproxy-node-client";
async function run() {
const toxiproxy = new Toxiproxy("http://localhost:8474");
const proxyBody: ICreateProxyBody = {
listen: "localhost:0", // Listen on an ephemeral port
name: "redis-proxy", // Give it a unique name
upstream: "redis:6379" // Target the actual Redis instance
};
try {
console.log(`Creating proxy for upstream: ${proxyBody.upstream}`);
const proxy = await toxiproxy.createProxy(proxyBody);
console.log(`Proxy '${proxy.name}' created, listening on ${proxy.listen}`);
const toxicBody: ICreateToxicBody<Bandwidth> = {
attributes: { rate: 1000 }, // 1000 KB/s
type: "bandwidth",
name: "limit-bandwidth"
};
console.log(`Adding bandwidth toxic (${toxicBody.attributes.rate} KB/s) to proxy '${proxy.name}'`);
const toxic = await proxy.addToxic(toxicBody);
console.log(`Toxic '${toxic.name}' of type '${toxic.type}' added.`);
// In a real scenario, you would now run your tests against proxy.listen
// e.g., connect your Redis client to proxy.listen, not redis:6379 directly.
// Clean up: remove the toxic and then the proxy
console.log(`Removing toxic '${toxic.name}'...`);
await proxy.deleteToxic(toxic.name);
console.log(`Toxic removed. Deleting proxy '${proxy.name}'...`);
await toxiproxy.deleteProxy(proxy.name);
console.log("Proxy deleted successfully.");
} catch (error: any) {
console.error("Error interacting with Toxiproxy:", error.message);
console.error("Ensure Toxiproxy server is running on http://localhost:8474");
console.error("You can run it via Docker: `docker run --rm -p 8474:8474 ghcr.io/shopify/toxiproxy:latest`");
}
}
run();