valve_

Per-tenant budgets

Create one valve per customer credential on demand, and share a single store between them.

In a multi-tenant integration, each customer connects their own account, and the provider meters each account separately. Give every credential its own valve id, created the first time it is needed.

valves.ts
import { createValve, type Valve } from 'valvejs';
import { redisStore } from 'valvejs/redis';

const store = redisStore({ client: redis }); // One store for every tenant.
const valves = new Map<string, Valve>();

export function shopifyValve(shop: string): Valve {
  let valve = valves.get(shop);
  if (!valve) {
    valve = createValve({
      id: `shopify:${shop}`,
      rate: { limit: 2, interval: '1s', burst: 40 },
      store,
      maxWait: '1m',
    });
    valves.set(shop, valve);
  }
  return valve;
}

export async function closeAll() {
  await Promise.all([...valves.values()].map((valve) => valve.close()));
  valves.clear();
}

Use it wherever you call the provider:

const res = await shopifyValve(shop).run(
  ({ signal }) =>
    fetch(`https://${shop}/admin/api/2025-01/orders.json`, {
      headers: { 'X-Shopify-Access-Token': tokens.get(shop)! },
      signal,
    }),
  { observe: (r) => r },
);

The rule here models a bucket of 40 requests that refills at 2 per second. Check the provider's documentation for your plan.

Why one store

A store is cheap, but each Redis store with a subscriber adds pub/sub subscriptions. Reuse one store for every valve in the process. Valves still keep separate budgets, because their ids differ.

For a memory store, sharing the instance is required: two valves with the same id only share a budget when they use the same store object.

Drop tenants you no longer need

Each valve holds a small local queue and its listeners. If tenants come and go, close and remove a valve when its tenant disconnects:

export async function removeTenant(shop: string) {
  const valve = valves.get(shop);
  valves.delete(shop);
  await valve?.close();
}

The budget state in Redis expires on its own once it is no longer used.

Ids must be safe

Ids are 1 to 200 characters and must not contain { or }. If a tenant key comes from user input, validate or hash it before building the id.

On this page