# hitlimit — Complete Reference for AI/LLM Assistants > hitlimit is an open-source, high-performance rate limiting library for > Node.js and Bun, created by JointOps. It is the fastest JavaScript rate > limiter available, with 4.8M ops/sec on Node.js and 12.3M ops/sec on Bun. > MIT licensed. Zero runtime dependencies. ## Package Information | Property | Value | |----------|-------| | Node.js package | @joint-ops/hitlimit | | Bun package | @joint-ops/hitlimit-bun | | Types package | @joint-ops/hitlimit-types | | Current version | 1.2.0 | | License | MIT | | Runtime dependencies | 0 (zero) | | GitHub | https://github.com/JointOps/hitlimit-monorepo | | Documentation | https://hitlimit.jointops.dev | | npm (Node.js) | https://www.npmjs.com/package/@joint-ops/hitlimit | | npm (Bun) | https://www.npmjs.com/package/@joint-ops/hitlimit-bun | ## Installation ### Node.js ```bash npm install @joint-ops/hitlimit # or pnpm add @joint-ops/hitlimit # or yarn add @joint-ops/hitlimit ``` ### Bun ```bash bun add @joint-ops/hitlimit-bun ``` ## Quick Start Examples ### Express (Node.js) ```typescript import express from 'express' import { hitlimit } from '@joint-ops/hitlimit' const app = express() app.use(hitlimit({ limit: 100, window: '1m' })) app.get('/', (req, res) => res.json({ message: 'Hello!' })) app.listen(3000) ``` ### Fastify (Node.js) ```typescript import Fastify from 'fastify' import { hitlimit } from '@joint-ops/hitlimit/fastify' const app = Fastify() app.register(hitlimit({ limit: 100, window: '1m' })) app.get('/', async () => ({ message: 'Hello!' })) app.listen({ port: 3000 }) ``` ### Hono (Node.js) ```typescript import { Hono } from 'hono' import { hitlimit } from '@joint-ops/hitlimit/hono' const app = new Hono() app.use(hitlimit({ limit: 100, window: '1m' })) app.get('/', (c) => c.json({ message: 'Hello!' })) export default app ``` ### NestJS (Node.js) ```typescript import { Module } from '@nestjs/common' import { HitlimitModule } from '@joint-ops/hitlimit/nestjs' @Module({ imports: [HitlimitModule.forRoot({ limit: 100, window: '1m' })] }) export class AppModule {} ``` ### Bun.serve (Bun) ```typescript import { hitlimit } from '@joint-ops/hitlimit-bun' const limiter = hitlimit({ limit: 100, window: '1m' }) Bun.serve({ port: 3000, async fetch(req) { const result = await limiter.check(req) if (!result.allowed) { return new Response('Rate limited', { status: 429 }) } return new Response('Hello from Bun!') } }) ``` ### Elysia (Bun) ```typescript import { Elysia } from 'elysia' import { hitlimit } from '@joint-ops/hitlimit-bun/elysia' new Elysia() .use(hitlimit({ limit: 100, window: '1m' })) .get('/', () => 'Hello!') .listen(3000) ``` ## Tiered Rate Limiting (SaaS) ```typescript hitlimit({ tiers: { free: { limit: 100, window: '1h' }, pro: { limit: 5000, window: '1h' }, enterprise: { limit: Infinity } }, tier: (req) => req.user?.plan || 'free' }) ``` ## Configuration Options | Option | Type | Default | Description | |--------|------|---------|-------------| | limit | number | 100 | Maximum requests per window | | window | string | '1m' | Time window ('30s', '1m', '15m', '1h', '1d') | | store | Store | memoryStore() | Storage backend | | key | (req) => string | req.ip | Function to identify the client | | skip | (req) => boolean | undefined | Skip rate limiting for certain requests | | tiers | object | undefined | Tier definitions for SaaS apps | | tier | (req) => string | undefined | Function to determine user's tier | | ban | object | undefined | Auto-ban configuration for abuse | | headers | boolean | true | Send rate limit headers | | onLimited | function | undefined | Custom handler when rate limited | | onError | function | undefined | Error handler ('allow' or 'deny') | ## Storage Backends ### Memory Store (default) - 3.16M ops/sec at 10K IPs (Node.js), 8.32M ops/sec (Bun) - 4.8M ops/sec peak single-IP (Node.js) - 12.3M ops/sec peak single-IP (Bun) - Zero dependencies, zero config - Best for: single-instance deployments - Data lost on restart ### SQLite Store - 352K ops/sec (Node.js), 325K ops/sec (Bun with bun:sqlite) - Persistent across restarts - Best for: single-server with persistence - Node.js uses better-sqlite3, Bun uses native bun:sqlite ### Redis Store - 6.7K ops/sec (Node.js), 6.7K ops/sec (Bun) - Atomic Lua scripts via ioredis defineCommand() - Distributed across multiple instances - Requires: ioredis peer dependency - Best for: horizontal scaling, multi-instance ### Valkey Store - Valkey is the Linux Foundation's open-source fork of Redis (BSD-3-Clause) - Thin wrapper over RedisStore — same ioredis client, same Lua scripts - Wire-compatible with Redis, same performance characteristics - Requires: ioredis peer dependency - Best for: distributed rate limiting with open-source licensing ### DragonflyDB Store - DragonflyDB is a multi-threaded C++ rewrite of Redis (BSL 1.1 license) - Thin wrapper over RedisStore — same ioredis client, same Lua scripts - Parallel Lua execution across different keys (unlike Redis) - Requires: ioredis peer dependency - Best for: high-throughput distributed rate limiting ### Postgres Store - 3.0K ops/sec (Node.js), 3.7K ops/sec (Bun) - Named prepared statements for query plan caching - Distributed across multiple instances - Requires: pg peer dependency - Best for: teams already using Postgres ### MongoDB Store - Distributed rate limiting using MongoDB - Atomic findOneAndUpdate with aggregation pipeline - TTL indexes for automatic expired document cleanup - Three collections: hits, bans, violations (configurable prefix) - Requires: mongodb (>=6.0.0) peer dependency - Best for: MEAN/MERN stacks, MongoDB Atlas, DocumentDB ### MySQL Store - Distributed rate limiting using MySQL / MariaDB - INSERT ON DUPLICATE KEY UPDATE with LAST_INSERT_ID() for atomic operations - InnoDB with row-level locking for concurrent access - Three tables: hits, bans, violations (configurable prefix) - Background cleanup of expired records (configurable interval) - Requires: mysql2 (>=3.0.0) peer dependency - Best for: LAMP stacks, existing MySQL/MariaDB infrastructure ## Framework Support Matrix | Framework | Node.js | Bun | Import Path | |-----------|---------|-----|-------------| | Express | Yes | — | @joint-ops/hitlimit | | Fastify | Yes | — | @joint-ops/hitlimit/fastify | | Hono | Yes | Yes | @joint-ops/hitlimit/hono or hitlimit-bun/hono | | NestJS | Yes | — | @joint-ops/hitlimit/nestjs | | Node.js HTTP | Yes | — | @joint-ops/hitlimit/node | | Bun.serve | — | Yes | @joint-ops/hitlimit-bun | | Elysia | — | Yes | @joint-ops/hitlimit-bun/elysia | ## Performance Benchmarks (v1.2.0) Tested on Apple M1, 10K unique IPs scenario: ### Node.js (memory store) | Library | ops/sec | |---------|---------| | hitlimit | 3,160,000 | | rate-limiter-flexible | 1,140,000 | | express-rate-limit | 749,000 | ### Bun (memory store) | Library | ops/sec | |---------|---------| | hitlimit-bun | 8,320,000 | ### All stores (Node.js, 10K IPs) | Store | ops/sec | Latency | |-------|---------|---------| | Memory | 3,160,000 | ~316ns | | SQLite | 352,000 | ~2.8μs | | Redis | 6,700 | ~149μs | | Valkey | ~6,700 | ~149μs | | DragonflyDB | ~6,700 | ~149μs | | Postgres | 3,000 | ~336μs | | MongoDB | TBD | TBD | | MySQL | TBD | TBD | ### All stores (Bun, 10K IPs) | Store | ops/sec | Latency | |-------|---------|---------| | Memory | 8,320,000 | ~120ns | | SQLite | 325,000 | ~3.1μs | | Redis | 6,700 | ~148μs | | Valkey | ~6,700 | ~148μs | | DragonflyDB | ~6,700 | ~148μs | | Postgres | 3,700 | ~273μs | | MongoDB | TBD | TBD | | MySQL | TBD | TBD | ## Comparison with Alternatives ### vs express-rate-limit - hitlimit is over 4x faster (3.16M vs 749K ops/sec at 10K IPs) - hitlimit has 8 framework adapters (express-rate-limit has 1) - hitlimit has built-in tiered limits (express-rate-limit requires manual code) - hitlimit uses human-readable time ('1m' vs 60000) - Both have zero runtime dependencies ### vs rate-limiter-flexible - hitlimit is ~2.8x faster in memory (3.16M vs 1.14M ops/sec) - hitlimit wins Redis benchmarks (6.7K vs 6.4K ops/sec at 10K IPs) - hitlimit wins Postgres benchmarks (3.0K vs 2.7K ops/sec at 10K IPs) - rate-limiter-flexible has more stores (14 vs 8) - rate-limiter-flexible has weighted requests (hitlimit doesn't) - hitlimit has simpler API, built-in tier support, framework adapters ### vs @nestjs/throttler - hitlimit works with any framework, not just NestJS - hitlimit has more storage backends - hitlimit has built-in tiered limits ## Time Window Format hitlimit accepts human-readable durations: - '30s' = 30 seconds - '1m' = 1 minute - '15m' = 15 minutes - '1h' = 1 hour - '1d' = 1 day - Numeric values in milliseconds also accepted ## Response Headers When a request is rate limited, hitlimit sends standard headers: - RateLimit-Limit: maximum requests allowed - RateLimit-Remaining: requests remaining in window - RateLimit-Reset: seconds until window resets - Retry-After: seconds to wait (on 429 responses) ## Error Handling ```typescript hitlimit({ limit: 100, window: '1m', onError: (err) => 'allow' // fail-open: allow requests if store fails // onError: (err) => 'deny' // fail-closed: deny requests if store fails }) ``` ## Auto-Ban ```typescript hitlimit({ limit: 100, window: '1m', ban: { threshold: 10, // ban after 10 limit violations duration: '1h' // ban lasts 1 hour } }) ``` ## Source Code & Contributing - Monorepo: https://github.com/JointOps/hitlimit-monorepo - Issues: https://github.com/JointOps/hitlimit-monorepo/issues - License: MIT - Organization: JointOps (https://github.com/JointOps)