{"_id":"@aproothi/sliding-rate-limiter-redis","_rev":"2-fa68c4fb5757481f1e83812b996ebc91","name":"@aproothi/sliding-rate-limiter-redis","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@aproothi/sliding-rate-limiter-redis","version":"1.0.0","keywords":["rate-limiter","redis","sliding-window","rate-limiting","throttle"],"author":{"name":"Akshit Proothi"},"license":"MIT","_id":"@aproothi/sliding-rate-limiter-redis@1.0.0","maintainers":[{"name":"aproothi","email":"aproothi@gmail.com"}],"dist":{"shasum":"391a26fd7f7f5caf517a4af850cb6b1f4b9c23f7","tarball":"https://registry.npmjs.org/@aproothi/sliding-rate-limiter-redis/-/sliding-rate-limiter-redis-1.0.0.tgz","fileCount":13,"integrity":"sha512-zBK8H6E/DDpXS19UgaVzLjwU5EtG/dLuQhqTC5/mCh4CV0nVSj7ZWpvT1TQKQDv2m2mq8XX/t7YYdxaUw/MLBw==","signatures":[{"sig":"MEUCIQDJPk7oc9EBAk2Pip0TH76F9yuoRZ5l06GQnYfcDcMXbAIgJEJKyfmrBzxi/ZB+s3mvmqeTxJRBZbrA5373TL106HA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":8807},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"b3dfda9d1e97b8213c763ed2454fa2032d6e54a6","scripts":{"test":"node --experimental-vm-modules node_modules/.bin/jest","bench":"node --loader ts-node/esm ../benchmarks/node/bench.ts","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"aproothi","email":"aproothi@gmail.com"},"_npmVersion":"10.8.2","description":"Redis-backed sliding-window rate limiter for Node.js","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ioredis":"^5.3.2","ts-jest":"^29.1.2","ts-node":"^10.9.2","typescript":"^5.4.5","@types/jest":"^29.5.12","@types/node":"^20.12.0","ioredis-mock":"^8.9.0"},"peerDependencies":{"ioredis":">=5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sliding-rate-limiter-redis_1.0.0_1777868532445_0.6064980494521652","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@aproothi/sliding-rate-limiter-redis","version":"1.0.1","description":"Redis-backed sliding-window rate limiter for Node.js","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build":"tsc","test":"node --experimental-vm-modules node_modules/.bin/jest","bench":"node --loader ts-node/esm ../benchmarks/node/bench.ts","prepublishOnly":"npm run build"},"keywords":["rate-limiter","redis","sliding-window","rate-limiting","throttle"],"author":{"name":"Akshit Proothi"},"license":"MIT","peerDependencies":{"ioredis":">=5.0.0"},"devDependencies":{"@types/jest":"^29.5.12","@types/node":"^20.12.0","ioredis":"^5.3.2","ioredis-mock":"^8.9.0","jest":"^29.7.0","ts-jest":"^29.1.2","ts-node":"^10.9.2","typescript":"^5.4.5"},"_id":"@aproothi/sliding-rate-limiter-redis@1.0.1","gitHead":"d0834814916b5ad66274aa05638957928f30b16a","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-9msxv9Y1OknTHjeKgWlrymN7rwQDHrsfRp+VusGW2rOqzImSx9poh5KdS0s2v04/sFjgEnYkGPpMCQxUozlJHg==","shasum":"382294901396f8140977670ac989c48cc43f445b","tarball":"https://registry.npmjs.org/@aproothi/sliding-rate-limiter-redis/-/sliding-rate-limiter-redis-1.0.1.tgz","fileCount":14,"unpackedSize":12996,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHEjjnmUihlngqg/oibQ7yxh5meLpprkZmlUIQt1o3HrAiEA/OAz1gXemwgDrYHj6Ow+QfJfPWtPBvc1XVkni2s6Ors="}]},"_npmUser":{"name":"aproothi","email":"aproothi@gmail.com"},"directories":{},"maintainers":[{"name":"aproothi","email":"aproothi@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sliding-rate-limiter-redis_1.0.1_1778040300400_0.9041282498569179"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-04T04:22:12.337Z","modified":"2026-05-06T04:05:00.648Z","1.0.0":"2026-05-04T04:22:12.591Z","1.0.1":"2026-05-06T04:05:00.537Z"},"author":{"name":"Akshit Proothi"},"license":"MIT","keywords":["rate-limiter","redis","sliding-window","rate-limiting","throttle"],"description":"Redis-backed sliding-window rate limiter for Node.js","maintainers":[{"name":"aproothi","email":"aproothi@gmail.com"}],"readme":"# sliding-rate-limiter-redis\n\nA Redis-backed **sliding window** rate limiter for Node.js.\n\n[![npm](https://img.shields.io/npm/v/@aproothi/sliding-rate-limiter-redis)](https://www.npmjs.com/package/@aproothi/sliding-rate-limiter-redis)\n[![Node.js CI](https://github.com/aproothi/sliding-rate-limiter-redis/actions/workflows/node-ci.yml/badge.svg)](https://github.com/aproothi/sliding-rate-limiter-redis/actions/workflows/node-ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/aproothi/sliding-rate-limiter-redis/blob/main/LICENSE)\n\n## Why sliding window?\n\nMost rate limiters use a fixed window (e.g. \"100 requests per minute, resetting on the clock\"). This creates a burst problem: a client can make 100 requests at 00:59 and another 100 at 01:00 — 200 requests in two seconds.\n\nThe **sliding window log** algorithm solves this by tracking the exact timestamp of every request in a Redis sorted set. Only requests within the last `window` seconds are counted, so the limit is enforced continuously rather than in discrete buckets.\n\nAll Redis operations execute atomically via a **Lua script**, so check-and-increment is race-condition free under high concurrency.\n\n## Installation\n\n```bash\nnpm install @aproothi/sliding-rate-limiter-redis ioredis\n```\n\nRequires `ioredis >= 5.0.0` and a running Redis instance.\n\n## Quick start\n\n```typescript\nimport Redis from 'ioredis';\nimport { SlidingWindowLimiter, RateLimitError } from '@aproothi/sliding-rate-limiter-redis';\n\nconst redis = new Redis();\nconst limiter = new SlidingWindowLimiter(redis, { limit: 100, window: 60 });\n\n// Non-throwing: inspect the result yourself\nconst result = await limiter.check('user:42');\nconsole.log(result);\n// { allowed: true, remaining: 99, retryAfter: null, resetAt: Date, limit: 100 }\n\n// Throwing: raises RateLimitError if the limit is exceeded\ntry {\n  await limiter.consume('user:42');\n} catch (err) {\n  if (err instanceof RateLimitError) {\n    res.set('Retry-After', String(err.retryAfter));\n    res.status(429).json({ error: 'Too Many Requests' });\n  }\n}\n```\n\n### Express middleware example\n\n```typescript\nfunction rateLimitMiddleware(limiter: SlidingWindowLimiter) {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    try {\n      const result = await limiter.consume(req.ip ?? 'unknown');\n      res.set('X-RateLimit-Limit', String(result.limit));\n      res.set('X-RateLimit-Remaining', String(result.remaining));\n      res.set('X-RateLimit-Reset', String(Math.floor(result.resetAt.getTime() / 1000)));\n      next();\n    } catch (err) {\n      if (err instanceof RateLimitError) {\n        res.set('Retry-After', String(err.retryAfter));\n        res.status(429).json({ error: 'Too Many Requests' });\n      } else {\n        next(err);\n      }\n    }\n  };\n}\n```\n\n## API\n\n### `new SlidingWindowLimiter(redis, options)`\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `redis` | `Redis` (ioredis) | Connected ioredis client |\n| `options.limit` | `number` | Maximum requests per window |\n| `options.window` | `number` | Window duration in **seconds** |\n| `options.keyPrefix` | `string` | Redis key prefix. Default: `\"rl\"` |\n\n### `check(identifier) → Promise<RateLimitResult>`\n\nRecords the request and returns a result. Does **not** throw on limit exceeded.\n\n### `consume(identifier) → Promise<RateLimitResult>`\n\nSame as `check()` but throws `RateLimitError` if the limit is exceeded.\n\n### `RateLimitResult`\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `allowed` | `boolean` | Whether the request was allowed |\n| `remaining` | `number` | Requests remaining in the current window |\n| `retryAfter` | `number \\| null` | Seconds until next request allowed; `null` if allowed |\n| `resetAt` | `Date` | When the current window resets |\n| `limit` | `number` | The configured limit |\n\n### `RateLimitError`\n\nThrown by `consume()` when the limit is exceeded. Carries `retryAfter`, `resetAt`, and `limit`.\n\n## Full documentation\n\nSee the [GitHub repository](https://github.com/aproothi/sliding-rate-limiter-redis) for architecture details, Python package, benchmarks, and more.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}