Quickstart
Create a valve, queue calls through it, and shut it down cleanly.
This page uses the default in-memory store, so you can run it without Redis. To share a budget between processes, finish here and then follow Share a quota across workers.
Before you begin
Install valve in a Node.js 20+ project with ESM enabled.
Create a valve
A valve is a named budget plus a local queue. This one allows 5 calls per second and at most 2 running at once.
import { createValve } from 'valvejs';
const github = createValve({
id: 'github:my-script',
rate: { limit: 5, interval: '1s' },
concurrency: 2,
});Queue work with run
run queues a function and resolves with its result. Types are preserved.
const users = ['octocat', 'torvalds', 'gaearon'];
const profiles = await Promise.all(
users.map((name) =>
github.run(async ({ signal }) => {
const res = await fetch(`https://api.github.com/users/${name}`, {
signal,
});
return (await res.json()) as { login: string; public_repos: number };
}),
),
);
console.log(profiles.map((p) => `${p.login}: ${p.public_repos}`));With the default burst: 1, starts are spaced evenly: one every 200ms.
Or wrap fetch
wrapFetch returns a drop-in fetch that goes through the valve, reads rate
limit headers from every response, and retries 429s after the pause ends for
GET, HEAD, OPTIONS, PUT and DELETE. POST and PATCH require an explicit opt-in;
see Requests with side effects.
const githubFetch = github.wrapFetch();
const res = await githubFetch('https://api.github.com/rate_limit');Close the valve
close stops accepting work and waits for running jobs. Pass drain: true
to also run everything still queued.
await github.close({ drain: true });Run it
npx tsx quickstart.tsWhat you learned
- A valve is identified by
id. Valves with the sameidand the same store share one budget. - The default store is a new private memory store per valve. Separate valves share nothing until you give them the same store instance.
runandwrapFetchqueue work;closeshuts the valve down.