Release candidate — 1.0.0-rc.9
StitchAPI
GuidesState & stores

The pluggable store

Swap the in-memory store for a shared one to back throttle, cache and sessions — get/set + TTL, plus the optional verbs that make a throttle fleet-wide.

Swap the in-memory store for a shared one when you want throttle counters and auth sessions to live outside a single process — back them with Redis, Postgres, or anything else by implementing two async methods, get and set, and attaching it via store. A few more are optional capabilities you add when your backend can do them atomically.

Example

import {  } from 'stitchapi';
import type { StitchStore } from 'stitchapi';

// A StitchStore is `get` + `set` with TTL — back it with anything. `increment`
// is one of the optional capabilities: add it only if your backend has an atomic counter.
const : StitchStore = {
    async () {
        /* read from your shared backend */
        return ;
    },
    async (, , ) {
        /* write, honoring ttl */
    },
    async (, ) {
        /* atomic counter with a TTL window */
        return 1;
    },
};

const  = ({
    : 'https://api.example.com',
    : '/users/{id}',
    : ,
});

Don't hand-roll one unless you need to — @stitchapi/redis is the first-party store: redisStore(fromIoredis(client)) (or fromNodeRedis), proven against the conformance.store kit.

Options

A store is the StitchStore contract. Two async methods are required: get(key) returns the value or undefined, and set(key, value, ttl?) writes with an optional expiry (set(key, undefined) deletes). Everything else is an optional capability the runtime uses when it's there:

  • increment(key, ttl?) — an atomic counter scoped to a TTL window, the throttle's fallback for pacing rate across processes;
  • reserve(...) — the atomic pacing cursor that makes rate exact fleet-wide;
  • lease(...) / release(...) — a semaphore that makes concurrency fleet-wide.

Leave out what your backend can't do atomically: the throttle falls back to per-process limits and emits one throttle.per-process info event saying so. The capability table shows what each store implements. The runtime keeps three kinds of state behind this interface: throttle state, the response cache, and auth session/token state (captured cookies and refreshed tokens).

The default is memoryStore() — single process, in-memory. It works until you run more than one worker, where each process keeps its own counters and its own sessions.

Point store at one shared backend and that splits apart: the rate counter becomes a single window every process increments, so the limit holds across the fleet — see Distributed throttle — and a captured session is written once and replayed everywhere, so workers don't each re-run the login — see Shared sessions.

Concurrency limits go fleet-wide only when the store implements the lease pair; without it they stay in-process. See Distributed throttle.

See also

On this page