Introduction
valve puts one rate limit in front of an API and shares it with every worker that calls that API.
Most providers enforce a quota per credential, not per process. When ten
workers each run their own limiter, they spend the same quota ten times over
and the provider starts returning 429 Too Many Requests. valve keeps the
budget in Redis or Valkey, so every process that uses the same valve id draws
from one shared limit. When the provider asks for a pause, every worker pauses.
import { createValve } from 'valvejs';
import { redisStore } from 'valvejs/redis';
const hubspot = createValve({
id: 'hubspot:portal-123',
rate: { limit: 190, interval: '10s' },
store: redisStore({ client: redis }),
});
const limitedFetch = hubspot.wrapFetch();
const response = await limitedFetch(
'https://api.hubapi.com/crm/v3/objects/contacts',
);Beta until 1.0. APIs may change between releases. Install valvejs with
plain version numbers through npm’s default channel.
What valve does
- Shares a budget across processes. Atomic Lua scripts check and spend every rule in one round trip, on the store's clock.
- Queues work locally. Jobs wait in-process with priorities, cost, concurrency limits, queue bounds, deadlines, and abort signals.
- Listens to the provider. valve reads
Retry-After, IETFRateLimit, and commonx-ratelimit-*headers, then pauses every worker sharing the budget. - Retries rate limit responses for idempotent methods.
wrapFetchretries a 429 after the shared pause ends, without losing its place in the queue. - Stays small. Zero runtime dependencies. ESM and CommonJS builds with
full TypeScript declarations. Bring your own
ioredisornode-redisclient.
When to use it
| You need to | Use |
|---|---|
| Respect a provider quota shared by several processes | valve with the Redis store |
| Space calls from a single script or service | valve with the default memory store |
| Limit parallel work inside one process, with no quota | p-limit or p-queue |
| Accept or reject inbound requests to your own server | An inbound rate limiter |
| Keep jobs across process restarts | A durable job queue, with valve around outbound calls |
valve limits when calls start. It does not persist queued jobs, retry arbitrary failures, or act as a circuit breaker.