v1.4.0 — MongoDB, MySQL, Valkey & DragonflyDB stores

Rate limiting that doesn't slow you down

7M+ ops/sec on a single core. Express, Fastify, Hono, NestJS, Bun.serve, Elysia — one API across every framework. ~8KB core, zero dependencies.

MIT License TypeScript-first Zero dependencies 8 storage backends
server.ts
import { hitlimit } from '@joint-ops/hitlimit'
import express from 'express'

const app = express()

app.use(hitlimit({
  limit: 100,
  window: '1m'
})) // That's it!
$ npm i @joint-ops/hitlimit

One library, every runtime

Same API on both runtimes. Switch between Node.js and Bun without changing your rate limiting code.

Not sure which to use? Both share the same API. Start with your runtime and switch anytime.

0.0M+
ops/sec
Node.js (10K IPs)
0.0M+
ops/sec
Bun (10K IPs)
<0ms
Latency
p95 response time
0
Dependencies
for core module

Numbers you can reproduce

Every benchmark is open source. Tested on Apple M1 Pro, single core. Run them yourself →

Store Node.js Bun Best For
Memory
4.08M ops/s 5.57M ops/s Highest throughput
SQLite
404K ops/s 372K ops/s Persistent storage
MongoDB
2.2K ops/s 2.1K ops/s Distributed (NoSQL)

10K unique IPs. Node.js v24 / Bun v1.3.7, Apple M1. Redis is network-bound. Our benchmarks — done our best to keep them fair. Full results → · Run them yourself

Built for SaaS

Your pricing page has tiers. So should your limiter.

With express-rate-limit, you create a separate middleware for every plan and wire them together yourself. With hitlimit, tiers are a config option.

Before
express-rate-limit
// Create separate limiters for each tier
const freeLimiter = rateLimit({
  windowMs: 60 * 60 * 1000, // 1 hour?
  max: 100
})

const proLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,
  max: 5000
})

const enterpriseLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,
  max: 100000
})

// Manual routing logic
app.use((req, res, next) => {
  const tier = getUserTier(req)
  if (tier === 'enterprise') {
    return enterpriseLimiter(req, res, next)
  } else if (tier === 'pro') {
    return proLimiter(req, res, next)
  }
  return freeLimiter(req, res, next)
})
25 lines Manual tier logic Milliseconds
After
hitlimit — Node.js & Bun
// Works with Express, Fastify, Hono, Bun.serve, Elysia...
hitlimit({
  tiers: {
    free: { limit: 100, window: '1h' },
    pro: { limit: 5000, window: '1h' },
    enterprise: { limit: Infinity }
  },
  tier: (req) => req.user?.plan || 'free'
})
7 lines Built-in tiers Human time Any runtime
3x less code for the same result

Why teams switch to hitlimit

Less code, faster throughput, and every store from memory to Postgres. Here's what you get out of the box.

Benchmarked, Not Claimed

5.96M ops/sec on Node.js. 7.73M ops/sec on Bun. Single-core, reproducible benchmarks you can run yourself.

One Line to Protect

hitlimit({ limit: 100, window: '1m' }) — that's your entire setup. Human-readable windows, sensible defaults, done.

Ships Nothing Extra

~8KB core bundle. Zero runtime dependencies. Tree-shakeable — you only pay for what you import.

Built-in SaaS Tiers

Define Free, Pro, and Enterprise limits in one config object. No manual routing, no separate middleware instances.

Your Framework, Covered

8 built-in adapters: Express, Fastify, Hono, NestJS, Node.js HTTP, Bun.serve, Elysia. Drop in and go.

Auto-Ban Abusers

Automatic ban escalation for repeat offenders. Configurable thresholds and durations. Your API fights back.

Honest side-by-side

We show what we don't support yet, too. No hidden footnotes.

Feature hitlimit express-rate-limit rate-limiter-flexible @nestjs/throttler
Framework adapters 8 built-in 1 DIY 1
Human time windows '15m' 900000 900 60000
Tiered limits (SaaS) Built-in Manual Manual Per-route
Bun native support bun:sqlite
Auto-ban on abuse
Storage backends 8 5 14 2
Standard headers Both Both Manual Legacy
MongoDB / MySQL / Postgres External
Weighted requests Planned
Built-in
Manual External / DIY
Planned On roadmap