@joint-ops/hitlimit 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
7
7
  [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@joint-ops/hitlimit)](https://bundlephobia.com/package/@joint-ops/hitlimit)
8
8
 
9
- > The fastest rate limiter for Node.js - Express, NestJS, and native HTTP | express-rate-limit alternative
9
+ > The fastest rate limiter for Node.js - Express, Fastify, NestJS, and native HTTP | express-rate-limit alternative
10
10
 
11
11
  **hitlimit** is a high-performance rate limiting middleware for Node.js applications. Protect your APIs from abuse, prevent brute force attacks, and throttle requests with sub-millisecond overhead. A faster, lighter alternative to express-rate-limit and rate-limiter-flexible.
12
12
 
@@ -32,9 +32,9 @@ hitlimit is designed for speed. Here's how it performs:
32
32
 
33
33
  | Store | Operations/sec | Avg Latency | Use Case |
34
34
  |-------|----------------|-------------|----------|
35
- | **Memory** | 400,000+ | 0.002ms | Single instance, no persistence |
36
- | **SQLite** | 35,000+ | 0.025ms | Single instance, persistence needed |
37
- | **Redis** | 12,000+ | 0.08ms | Multi-instance, distributed |
35
+ | **Memory** | 2,320,000+ | 0.43μs | Single instance, no persistence |
36
+ | **SQLite** | 393,000+ | 2.54μs | Single instance, persistence needed |
37
+ | **Redis** | 6,500+ | 153μs | Multi-instance, distributed |
38
38
 
39
39
  ### vs Competitors
40
40
 
@@ -44,6 +44,8 @@ hitlimit is designed for speed. Here's how it performs:
44
44
  | rate-limiter-flexible | 1,630,000 | ~155KB |
45
45
  | express-rate-limit | 1,220,000 | ~66KB |
46
46
 
47
+ > **Note:** Benchmark results vary by hardware and environment. Run your own benchmarks to see results on your specific setup.
48
+
47
49
  ### HTTP Overhead
48
50
 
49
51
  | Framework | Without Limiter | With hitlimit | Overhead |
@@ -74,6 +76,12 @@ pnpm add @joint-ops/hitlimit
74
76
  yarn add @joint-ops/hitlimit
75
77
  ```
76
78
 
79
+ For Fastify, also install peer dependencies:
80
+
81
+ ```bash
82
+ npm install fastify fastify-plugin
83
+ ```
84
+
77
85
  ## Quick Start
78
86
 
79
87
  ### Express Rate Limiting
@@ -97,6 +105,23 @@ app.get('/api', (req, res) => res.json({ status: 'ok' }))
97
105
  app.listen(3000)
98
106
  ```
99
107
 
108
+ ### Fastify Rate Limiting
109
+
110
+ ```typescript
111
+ import Fastify from 'fastify'
112
+ import { hitlimit } from '@joint-ops/hitlimit/fastify'
113
+
114
+ const app = Fastify()
115
+
116
+ await app.register(hitlimit, {
117
+ limit: 100,
118
+ window: '1m'
119
+ })
120
+
121
+ app.get('/api', () => ({ status: 'ok' }))
122
+ await app.listen({ port: 3000 })
123
+ ```
124
+
100
125
  ### NestJS Rate Limiting
101
126
 
102
127
  ```typescript
@@ -130,8 +155,12 @@ import { createHitLimit } from '@joint-ops/hitlimit/node'
130
155
  const limiter = createHitLimit({ limit: 100, window: '1m' })
131
156
 
132
157
  const server = http.createServer(async (req, res) => {
133
- const result = await limiter(req, res)
134
- if (!result.allowed) return // Already sent 429
158
+ const result = await limiter.check(req)
159
+ if (!result.allowed) {
160
+ res.writeHead(429, { 'Content-Type': 'application/json', ...result.headers })
161
+ res.end(JSON.stringify(result.body))
162
+ return
163
+ }
135
164
 
136
165
  res.writeHead(200)
137
166
  res.end('Hello!')
@@ -184,11 +213,12 @@ Rate limit by IP address, user ID, API key, or any custom identifier.
184
213
  hitlimit({
185
214
  key: (req) => {
186
215
  // By API key
187
- if (req.headers['x-api-key']) return req.headers['x-api-key']
216
+ const apiKey = req.headers['x-api-key']
217
+ if (apiKey) return String(apiKey)
188
218
  // By user ID
189
219
  if (req.user?.id) return `user:${req.user.id}`
190
220
  // Fallback to IP
191
- return req.ip
221
+ return req.ip || 'unknown'
192
222
  }
193
223
  })
194
224
  ```
@@ -216,7 +246,7 @@ hitlimit({
216
246
  window: '1m', // Time window: 30s, 15m, 1h, 1d (default: '1m')
217
247
 
218
248
  // Custom key extraction
219
- key: (req) => req.ip,
249
+ key: (req) => req.ip || 'unknown',
220
250
 
221
251
  // Tiered rate limits
222
252
  tiers: {
@@ -393,4 +423,4 @@ MIT - Use freely in personal and commercial projects.
393
423
 
394
424
  ## Keywords
395
425
 
396
- rate limit, rate limiter, rate limiting, express rate limit, express middleware, express-rate-limit, express-rate-limit alternative, nestjs rate limit, nestjs throttler, @nestjs/throttler alternative, nestjs guard, nodejs rate limit, node rate limiter, api rate limiting, throttle requests, request throttling, api throttling, ddos protection, brute force protection, redis rate limit, memory rate limit, sqlite rate limit, sliding window, fixed window, token bucket, leaky bucket, rate-limiter-flexible alternative, api security, request limiter, http rate limit, express slow down, api protection, login protection, authentication rate limit
426
+ rate limit, rate limiter, rate limiting, express rate limit, express middleware, express-rate-limit, express-rate-limit alternative, fastify rate limit, fastify plugin, fastify-rate-limit alternative, nestjs rate limit, nestjs throttler, @nestjs/throttler alternative, nestjs guard, nodejs rate limit, node rate limiter, api rate limiting, throttle requests, request throttling, api throttling, ddos protection, brute force protection, redis rate limit, memory rate limit, sqlite rate limit, sliding window, fixed window, token bucket, leaky bucket, rate-limiter-flexible alternative, api security, request limiter, http rate limit, express slow down, api protection, login protection, authentication rate limit
@@ -0,0 +1,6 @@
1
+ import type { FastifyInstance, FastifyRequest } from 'fastify';
2
+ import type { HitLimitOptions } from '@joint-ops/hitlimit-types';
3
+ declare function hitlimitPlugin(fastify: FastifyInstance, options: HitLimitOptions<FastifyRequest>): Promise<void>;
4
+ export declare const hitlimit: typeof hitlimitPlugin;
5
+ export {};
6
+ //# sourceMappingURL=fastify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastify.d.ts","sourceRoot":"","sources":["../src/fastify.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAgB,MAAM,SAAS,CAAA;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAShE,iBAAe,cAAc,CAC3B,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,eAAe,CAAC,cAAc,CAAC,iBA8BzC;AAED,eAAO,MAAM,QAAQ,uBAGnB,CAAA"}
@@ -0,0 +1,40 @@
1
+ import fp from 'fastify-plugin';
2
+ import { resolveConfig } from './core/config.js';
3
+ import { checkLimit } from './core/limiter.js';
4
+ import { memoryStore } from './stores/memory.js';
5
+ function getDefaultKey(req) {
6
+ return req.ip || 'unknown';
7
+ }
8
+ async function hitlimitPlugin(fastify, options) {
9
+ const store = options.store ?? memoryStore();
10
+ const config = resolveConfig(options, store, getDefaultKey);
11
+ fastify.addHook('onRequest', async (request, reply) => {
12
+ if (config.skip) {
13
+ const shouldSkip = await config.skip(request);
14
+ if (shouldSkip)
15
+ return;
16
+ }
17
+ try {
18
+ const result = await checkLimit(config, request);
19
+ for (const [key, value] of Object.entries(result.headers)) {
20
+ reply.header(key, value);
21
+ }
22
+ if (!result.allowed) {
23
+ reply.status(429).send(result.body);
24
+ return;
25
+ }
26
+ }
27
+ catch (error) {
28
+ const action = await config.onStoreError(error, request);
29
+ if (action === 'deny') {
30
+ reply.status(429).send({ hitlimit: true, message: 'Rate limit error' });
31
+ return;
32
+ }
33
+ }
34
+ });
35
+ }
36
+ export const hitlimit = fp(hitlimitPlugin, {
37
+ name: 'hitlimit',
38
+ fastify: '>=4.0.0'
39
+ });
40
+ //# sourceMappingURL=fastify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastify.js","sourceRoot":"","sources":["../src/fastify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAG/B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEhD,SAAS,aAAa,CAAC,GAAmB;IACxC,OAAO,GAAG,CAAC,EAAE,IAAI,SAAS,CAAA;AAC5B,CAAC;AAED,KAAK,UAAU,cAAc,CAC3B,OAAwB,EACxB,OAAwC;IAExC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,WAAW,EAAE,CAAA;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;IAE3D,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QAClF,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YAC7C,IAAI,UAAU;gBAAE,OAAM;QACxB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;YAEhD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1D,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;YAC1B,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBACnC,OAAM;YACR,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,KAAc,EAAE,OAAO,CAAC,CAAA;YACjE,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACtB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAA;gBACvE,OAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,CAAC,cAAc,EAAE;IACzC,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,SAAS;CACnB,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@joint-ops/hitlimit",
3
- "version": "1.0.3",
4
- "description": "Fast rate limiting middleware for Express, NestJS & Node.js - API throttling, brute force protection, request limiting",
3
+ "version": "1.0.5",
4
+ "description": "Fast rate limiting middleware for Express, Fastify, NestJS & Node.js - API throttling, brute force protection, request limiting",
5
5
  "author": {
6
6
  "name": "Shayan M Hussain",
7
7
  "email": "shayanhussain48@gmail.com",
@@ -32,6 +32,8 @@
32
32
  "nest-throttler",
33
33
  "fastify",
34
34
  "fastify-rate-limit",
35
+ "fastify-plugin",
36
+ "fastify-middleware",
35
37
  "hono",
36
38
  "hono-rate-limit",
37
39
  "hono-middleware",
@@ -87,6 +89,14 @@
87
89
  "types": "./dist/node.d.ts",
88
90
  "import": "./dist/node.js"
89
91
  },
92
+ "./fastify": {
93
+ "types": "./dist/fastify.d.ts",
94
+ "import": "./dist/fastify.js"
95
+ },
96
+ "./stores/memory": {
97
+ "types": "./dist/stores/memory.d.ts",
98
+ "import": "./dist/stores/memory.js"
99
+ },
90
100
  "./stores/sqlite": {
91
101
  "types": "./dist/stores/sqlite.d.ts",
92
102
  "import": "./dist/stores/sqlite.js"
@@ -119,11 +129,13 @@
119
129
  "test:watch": "vitest"
120
130
  },
121
131
  "dependencies": {
122
- "@joint-ops/hitlimit-types": "1.0.3"
132
+ "@joint-ops/hitlimit-types": "1.0.5"
123
133
  },
124
134
  "peerDependencies": {
125
135
  "@nestjs/common": ">=8.0.0",
126
136
  "@nestjs/core": ">=8.0.0",
137
+ "fastify": ">=4.0.0",
138
+ "fastify-plugin": ">=4.0.0",
127
139
  "better-sqlite3": ">=9.0.0",
128
140
  "ioredis": ">=5.0.0",
129
141
  "pino": ">=8.0.0",
@@ -136,6 +148,12 @@
136
148
  "@nestjs/core": {
137
149
  "optional": true
138
150
  },
151
+ "fastify": {
152
+ "optional": true
153
+ },
154
+ "fastify-plugin": {
155
+ "optional": true
156
+ },
139
157
  "better-sqlite3": {
140
158
  "optional": true
141
159
  },
@@ -160,6 +178,8 @@
160
178
  "@types/supertest": "^6.0.0",
161
179
  "better-sqlite3": "^11.0.0",
162
180
  "express": "^4.18.0",
181
+ "fastify": "^5.7.4",
182
+ "fastify-plugin": "^5.1.0",
163
183
  "ioredis": "^5.3.0",
164
184
  "pino": "^10.3.0",
165
185
  "reflect-metadata": "^0.2.0",