Catalog npm
Back to packageRELEASE DETAILS

0.4.3

npm

@021.is/spine-ratelimit

Postgres-backed sliding-window rate limiter. Sliding-window per-key buckets. No Redis required.

Published28 May 2026
Direct dependencies1
Transitive dependenciesNot calculated

README

Current package documentation

@021.is/spine-ratelimit

Postgres-backed sliding-window rate limiter. Sliding-window per-key buckets. Multi-window in parallel (1m + 1h + 1d). No Redis required.

Use

import { RateLimiter, makePrismaStore } from "@021.is/spine-ratelimit";

const limiter = new RateLimiter({ store: makePrismaStore(prisma) });

// On every API request:
await limiter.hit({
  subject: `api-key:${keyId}`,
  limits: [
    { window: "1m", max: 60 },
    { window: "1d", max: 100_000 },
  ],
}); // throws RateLimitedException → spine-errors maps to 429 + Retry-After

Prisma schema

model RateLimitBucket {
  subject     String
  window      String
  bucketStart DateTime
  count       Int      @default(0)
  @@id([subject, window, bucketStart])
}

Behavior

  • Atomic UPSERT + increment per (subject, window, bucketStart). Postgres guarantees concurrency safety.
  • Multi-window in parallel: a single hit() checks 1m AND 1d limits at once.
  • RateLimitedException.retryAfterSec reflects the tightest window — the one that has the least time until reset.
  • Subject isolation: different subjects share zero state.

In-memory adapter (for tests)

import { makeMemoryStore } from "@021.is/spine-ratelimit";
const limiter = new RateLimiter({ store: makeMemoryStore() });

Why Postgres, not Redis

  • One less infrastructure component
  • Atomic guarantees come free with Postgres
  • For typical 021 workloads (≤ 1k req/s per app), Postgres handles it without breaking a sweat
  • Cleanup: prune buckets older than your widest window via a daily cron

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Declared dependencies 1

Native requirements
1 direct targetsNot calculated transitive count
Package / targetRequirementRelationship
@021.is/spine-errors
Environment
{
  "local_name": "@021.is/spine-errors",
  "alias": false,
  "peer_metadata": null
}
^0.4.3runtime

Distribution files

1 collected
Distribution 1

Source distribution

Source file
Checksums from source
{
  "integrity": "sha512-92Stx/iliJPTRTioIQ2/Wta4k6ZL/s0COnM9UzjysfZ0P0Q901OEZ3ImQmtnrULoREEaZNSEZPLKj61NS+LTNg==",
  "shasum": "cbffe7a0519032f55b78ba3d9aeea055fe101d7b",
  "tarball": "https://registry.npmjs.org/@021.is/spine-ratelimit/-/spine-ratelimit-0.4.3.tgz",
  "fileCount": 5,
  "unpackedSize": 9203,
  "signatures": [
    {
      "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U",
      "sig": "MEYCIQC5GXKGvQ3aCDp7VN8qGc1CO2Qji3InIy7YvCwBoHuY1QIhAOqHE7r6CupBJVkpgc8XoNZdFeWxR89L9zQCqe4n6sYB"
    }
  ]
}

Repository

PENDING
021is/spine-ts

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 22:00 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package_version:d54289e27eab6cc800aeee7a71eeac1ad0b04701b6a75fee1e111e1f20f42281

Other collected versions

0.4.028 MayView all collected versions →