
Security News
Socket Releases Free Certified Patches for Critical vm2 Sandbox Escape
A critical vm2 sandbox escape can allow untrusted JavaScript to break isolation and execute commands on the host Node.js process.
@joint-ops/hitlimit
Advanced tools
Fast rate limiting middleware for Express, Fastify, NestJS & Node.js - API throttling, brute force protection, request limiting
Rate limiting that doesn't slow you down.
4.08M ops/sec at 10K unique IPs. Zero dependencies. One line to protect any Node.js API.
npm install @joint-ops/hitlimit
app.use(hitlimit()) // That's it. 100 req/min per IP.
Works with Express, Fastify, Hono, NestJS, and native HTTP β no config, no adapters to install, no boilerplate.
Docs Β· GitHub Β· Benchmarks
import express from 'express'
import { hitlimit } from '@joint-ops/hitlimit'
const app = express()
app.use(hitlimit({ limit: 100, window: '15m' }))
import { hitlimit } from '@joint-ops/hitlimit/fastify'
await app.register(hitlimit, { limit: 100, window: '1m' })
import { hitlimit } from '@joint-ops/hitlimit/hono'
app.use(hitlimit({ limit: 100, window: '1m' }))
import { HitLimitModule, HitLimitGuard } from '@joint-ops/hitlimit/nest'
@Module({
imports: [HitLimitModule.register({ limit: 100, window: '1m' })],
providers: [{ provide: APP_GUARD, useClass: HitLimitGuard }]
})
export class AppModule {}
import { createHitLimit } from '@joint-ops/hitlimit/node'
const limiter = createHitLimit({ limit: 100, window: '1m' })
const server = http.createServer(async (req, res) => {
const result = await limiter.check(req)
if (!result.allowed) return res.writeHead(429).end()
res.end('OK')
})
Tiered limits β Free, Pro, Enterprise in 4 lines:
hitlimit({
tiers: { free: { limit: 100, window: '1h' }, pro: { limit: 5000, window: '1h' } },
tier: (req) => req.user?.plan || 'free'
})
Auto-ban β Ban repeat offenders automatically:
hitlimit({ limit: 100, window: '1m', ban: { threshold: 5, duration: '15m' } })
Custom keys β Rate limit by IP, user, API key, or anything:
hitlimit({ key: (req) => req.headers['x-api-key'] || req.ip })
Shared limits β Team-wide quotas with group:
hitlimit({ limit: 10000, window: '1h', group: (req) => req.user.teamId })
Skip rules β Whitelist health checks, admins, internal routes:
hitlimit({ skip: (req) => req.path === '/health' || req.user?.role === 'admin' })
Every store is built in. Swap one line β your rate limiting code stays the same.
Single Server Multi-Server
ββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
β Memory β SQLite β β Redis β Postgres β
β (default) β β Valkey β MongoDB β
β β β Dragonfly MySQL β
ββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
No persistence Your existing infrastructure, zero lock-in
| Store | Ops/sec | Latency | When to use |
|---|---|---|---|
| Memory | 4,082,874 | 245ns | Single server, no persistence needed |
| SQLite | 404,135 | 2.5ΞΌs | Single server, need persistence |
| MongoDB | 2,161 | 462.8ΞΌs | Multi-server / NoSQL infrastructure |
Redis, Valkey, DragonflyDB, Postgres, and MySQL are network-bound (~200β3,500 ops/sec). Benchmarks at hitlimit.jointops.dev/docs/benchmarks.
import { hitlimit } from '@joint-ops/hitlimit'
import { ______Store } from '@joint-ops/hitlimit/stores/______'
app.use(hitlimit({ store: ______Store({ /* connection config */ }) }))
app.use(hitlimit()) // that's it
import { sqliteStore } from '@joint-ops/hitlimit/stores/sqlite'
app.use(hitlimit({ store: sqliteStore({ path: './ratelimit.db' }) }))
Peer dep: better-sqlite3
import { redisStore } from '@joint-ops/hitlimit/stores/redis'
app.use(hitlimit({ store: redisStore({ url: 'redis://localhost:6379' }) }))
Peer dep: ioredis
import { valkeyStore } from '@joint-ops/hitlimit/stores/valkey'
app.use(hitlimit({ store: valkeyStore({ url: 'redis://localhost:6379' }) }))
Peer dep: ioredis
import { dragonflyStore } from '@joint-ops/hitlimit/stores/dragonfly'
app.use(hitlimit({ store: dragonflyStore({ url: 'redis://localhost:6379' }) }))
Peer dep: ioredis
import { postgresStore } from '@joint-ops/hitlimit/stores/postgres'
app.use(hitlimit({ store: postgresStore({ url: 'postgres://localhost:5432/mydb' }) }))
Peer dep: pg
import { mongoStore } from '@joint-ops/hitlimit/stores/mongodb'
import { MongoClient } from 'mongodb'
const client = new MongoClient('mongodb://localhost:27017')
const db = client.db('myapp')
app.use(hitlimit({ store: mongoStore({ db }) }))
Peer dep: mongodb
import { mysqlStore } from '@joint-ops/hitlimit/stores/mysql'
import mysql from 'mysql2/promise'
const pool = mysql.createPool('mysql://root@localhost:3306/mydb')
app.use(hitlimit({ store: mysqlStore({ pool }) }))
Peer dep: mysql2
| Library | Ops/sec | |
|---|---|---|
| hitlimit | 4,082,874 | ββββββββββββββββββββ |
| rate-limiter-flexible | 1,261,659 | ββββββ |
| express-rate-limit | 824,030 | ββββ |
This is the memory store comparison β the default for all three libraries. For Redis, Postgres, and cross-store breakdowns, see the full benchmark results. Controlled-environment microbenchmarks with transparent methodology. We report scenarios where competitors beat us. Run them yourself.
// express-rate-limit β hitlimit
- import rateLimit from 'express-rate-limit'
- app.use(rateLimit({ windowMs: 60000, max: 100 }))
+ import { hitlimit } from '@joint-ops/hitlimit'
+ app.use(hitlimit({ window: '1m', limit: 100 }))
Full migration guide in the docs.
MIT
FAQs
Fast rate limiting middleware for Express, Fastify, NestJS & Node.js - API throttling, brute force protection, request limiting
We found that @joint-ops/hitlimit demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
A critical vm2 sandbox escape can allow untrusted JavaScript to break isolation and execute commands on the host Node.js process.

Research
Five malicious NuGet packages impersonate Chinese .NET libraries to deploy a stealer targeting browser credentials, crypto wallets, SSH keys, and local files.

Security News
pnpm 11 turns on a 1-day Minimum Release Age and blocks exotic subdeps by default, adding safeguards against fast-moving supply chain attacks.