{"id":13789732,"url":"https://github.com/rayriffy/elysia-rate-limit","last_synced_at":"2026-05-10T07:09:21.618Z","repository":{"id":196348944,"uuid":"611185484","full_name":"rayriffy/elysia-rate-limit","owner":"rayriffy","description":"Lightweight rate limiter plugin for Elysia.js","archived":false,"fork":false,"pushed_at":"2026-01-31T12:18:31.000Z","size":258,"stargazers_count":192,"open_issues_count":0,"forks_count":13,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-31T14:34:45.056Z","etag":null,"topics":["api","bun","elysia","elysia-plugin","middleware","rate-limit","rate-limiting"],"latest_commit_sha":null,"homepage":"https://npm.im/elysia-rate-limit","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rayriffy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-03-08T09:50:48.000Z","updated_at":"2026-01-31T12:18:35.000Z","dependencies_parsed_at":"2025-01-15T15:16:09.213Z","dependency_job_id":"662be8ba-704e-45e0-bfec-1b999367ddcd","html_url":"https://github.com/rayriffy/elysia-rate-limit","commit_stats":{"total_commits":67,"total_committers":5,"mean_commits":13.4,"dds":0.3731343283582089,"last_synced_commit":"7399d57766e98c883a1ef8c42dbd9ddcf7f2e963"},"previous_names":["rayriffy/elysia-rate-limit"],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/rayriffy/elysia-rate-limit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rayriffy%2Felysia-rate-limit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rayriffy%2Felysia-rate-limit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rayriffy%2Felysia-rate-limit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rayriffy%2Felysia-rate-limit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rayriffy","download_url":"https://codeload.github.com/rayriffy/elysia-rate-limit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rayriffy%2Felysia-rate-limit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30486711,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-14T01:54:10.014Z","status":"online","status_checked_at":"2026-03-14T02:00:06.612Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","bun","elysia","elysia-plugin","middleware","rate-limit","rate-limiting"],"created_at":"2024-08-03T22:00:32.331Z","updated_at":"2026-05-10T07:09:21.599Z","avatar_url":"https://github.com/rayriffy.png","language":"TypeScript","funding_links":[],"categories":["Plugins"],"sub_categories":[],"readme":"# Elysia Rate Limit\n\nLightweight rate limiter plugin for [Elysia.js](https://elysiajs.com/)\n\n[![NPM Version](https://img.shields.io/npm/v/elysia-rate-limit)](https://www.npmjs.com/package/elysia-rate-limit)\n[![NPM Downloads](https://img.shields.io/npm/dw/elysia-rate-limit)](https://www.npmjs.com/package/elysia-rate-limit)\n[![NPM License](https://img.shields.io/npm/l/elysia-rate-limit)](https://www.npmjs.com/package/elysia-rate-limit)\n\n## Install\n\n```\nbun add elysia-rate-limit\n```\n\nIf you're using Bun v1.0.3 or lower, `elysia-rate-limit` v2.0.0 or higher will not be compatible. Please use `elysia-rate-limit` [v1.3.0](https://github.com/rayriffy/elysia-rate-limit/releases/tag/v1.3.0) instead.\n\n## Compatibility\n\nAs long as you're on the latest version of Bun, and Elysia.\nUsing the latest version of `elysia-rate-limit` would works just fine.\nHowever, please refer to the following table to determine which version to use.\n\n| Plugin version | Requirements                 |\n|----------------|------------------------------|\n| 3.0.0+         | Bun \u003e 1.0.3, Elysia \u003e= 1.0.0 |\n| 2.0.0 - 2.2.0  | Bun \u003e 1.0.3, Elysia \u003c 1.0.0  |\n| 1.0.2 - 1.3.0  | Bun \u003c= 1.0.3, Elysia \u003c 1.0.0 |\n\n## Usage\n\nCheck out full sample at [`example`](example/basic.ts)\n\n```ts\nimport { Elysia } from 'elysia'\nimport { rateLimit } from 'elysia-rate-limit'\n\nnew Elysia().use(rateLimit()).listen(3000)\n```\n\n## Configuration\n\n### duration\n\n`number | ((key: string, request: ExtendedRequest) =\u003e number | Promise\u003cnumber\u003e)`\n\nDefault: `60000`\n\nDuration for requests to be remembered in **milliseconds**.\nAlso used in the `Retry-After` header when the limit is reached.\n\nCan be a static number or a dynamic function that returns the duration based on the client key and request. The function receives:\n- `key`: The generated client key (e.g., IP address)\n- `request`: The request object with cookies attached\n\n\u003e **Note:** The dynamic value is resolved **when a new window opens** for a given key. Changing the duration mid-window has no effect on the current window — it will apply on the next window.\n\n\u003cdetails\u003e\n\u003csummary\u003eExample for static \u003ccode\u003eduration\u003c/code\u003e\u003c/summary\u003e\n\n```ts\nnew Elysia().use(\n  rateLimit({\n    duration: 60_000, // 1-minute window\n  })\n)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExample for dynamic \u003ccode\u003eduration\u003c/code\u003e\u003c/summary\u003e\n\n```ts\nnew Elysia().use(\n  rateLimit({\n    duration: (key, request) =\u003e {\n      // Give premium users a shorter window\n      const isPremium = request.headers.get('X-User-Tier') === 'premium'\n      return isPremium ? 10_000 : 60_000\n    },\n  })\n)\n```\n\n```ts\nnew Elysia().use(\n  rateLimit({\n    duration: async (key, request) =\u003e {\n      // Fetch user tier from database\n      const userTier = await getUserTier(key)\n      return userTier === 'premium' ? 10_000 : 60_000\n    },\n  })\n)\n```\n\u003c/details\u003e\n\n### max\n\n`number | ((key: string, request: ExtendedRequest) =\u003e number | Promise\u003cnumber\u003e)`\n\nDefault: `10`\n\nMaximum of request to be allowed during 1 `duration` timeframe.\n\nCan be a static number or a dynamic function that returns the max based on the client key and request. The function receives:\n- `key`: The generated client key (e.g., IP address)\n- `request`: The request object with cookies attached\n\n\u003cdetails\u003e\n\u003csummary\u003eExample for static \u003ccode\u003emax\u003c/code\u003e\u003c/summary\u003e\n\n```ts\nnew Elysia().use(\n  rateLimit({\n    max: 100, // Allow 100 requests per duration\n  })\n)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExample for dynamic \u003ccode\u003emax\u003c/code\u003e\u003c/summary\u003e\n\n```ts\nnew Elysia().use(\n  rateLimit({\n    max: (key, request) =\u003e {\n      // Give premium users higher limits\n      const isPremium = request.headers.get('X-User-Tier') === 'premium'\n      return isPremium ? 1000 : 100\n    },\n  })\n)\n```\n\n```ts\nnew Elysia().use(\n  rateLimit({\n    max: async (key, request) =\u003e {\n      // Fetch user tier from database\n      const userTier = await getUserTier(key)\n      return userTier === 'premium' ? 1000 : 100\n    },\n  })\n)\n```\n\u003c/details\u003e\n\n### errorResponse\n\n`string | Response | Error`\n\nDefault: `rate-limit reached`\n\nResponse to be sent when the rate limit is reached.\n\nIf you define a value as a string,\nthen it will be sent as a plain text response with status code 429. If you define a value as a `Response` object,\nthen it will be sent as is.\nAnd if you define a value as an `Error` object, then it will be thrown as an error.\n\n\u003cdetails\u003e\n\u003csummary\u003eExample for \u003ccode\u003eResponse\u003c/code\u003e object response\u003c/summary\u003e\n\n```ts\nnew Elysia()\n  .use(\n    rateLimit({\n      errorResponse: new Response(\"rate-limited\", {\n        status: 429,\n        headers: new Headers({\n          'Content-Type': 'text/plain',\n          'Custom-Header': 'custom',\n        }),\n      }),\n    })\n  )\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExample for \u003ccode\u003eError\u003c/code\u003e object response\u003c/summary\u003e\n\n```ts\nimport { HttpStatusEnum } from 'elysia-http-status-code/status'\n\nexport class RateLimitError extends Error {\n  constructor(\n    public message: string = 'rate-limited',\n    public detail: string = '',\n    public status: number = HttpStatusEnum.HTTP_429_TOO_MANY_REQUESTS // or just 429\n  ) {\n    super(message)\n  }\n}\n\nnew Elysia()\n  .use(\n    rateLimit({\n      errorResponse: new RateLimitError(),\n    })\n  )\n  // use with error hanlder\n  .error({\n    rateLimited: RateLimitError,\n  })\n  .onError({ as: 'global' }, ({ code }) =\u003e {\n    switch (code) {\n      case 'rateLimited':\n        return code\n        break\n    }\n  })\n```\n\n\u003c/details\u003e\n\n### scoping\n\n`'global' | 'scoped'`\n\nDefault: `'global'`\n\nSometimes you may want\nto only apply rate limit plugin to curtain Elysia instance.\nThis option will allow you\nto choose scope `local` apply to only current instance and descendant only.\nBut by default,\nrate limit plugin will apply to all instances that apply the plugin.\n\nRead more : [Scope - ElysiaJS | ElysiaJS](https://elysiajs.com/essential/plugin.html#scope-level)\n\n### generator\n\n`\u003cT extends object\u003e(equest: Request, server: Server | null, derived: T) =\u003e MaybePromise\u003cstring\u003e`\n\nCustom key generator to categorize client requests, return as a string. By default, this plugin will categorize client by their IP address via [`server.requestIP()` function](https://github.com/oven-sh/bun/pull/6165).\n\nIf you deploy your server behind a proxy (i.e. NGINX, Cloudflare), you may need to implement your own generator to get client's real IP address.\n\n```js\n// IMPORTANT: Only use this if your server is behind Cloudflare AND\n// you've restricted access to only Cloudflare IPs\nconst cloudflareGenerator = (req, server) =\u003e {\n  // Verify the request is coming from Cloudflare\n  // In production, you should maintain a list of Cloudflare IP ranges\n  // and verify the request IP is in that range\n  const isFromCloudflare = verifyCloudflareIP(server?.requestIP(req)?.address)\n  \n  if (isFromCloudflare) {\n    // Only trust CF-Connecting-IP if the request comes from Cloudflare\n    return req.headers.get('CF-Connecting-IP') ?? server?.requestIP(req)?.address ?? ''\n  }\n  \n  // For non-Cloudflare requests, use the direct IP\n  return server?.requestIP(req)?.address ?? ''\n}\n\n// Example function to verify Cloudflare IPs (implement this based on your needs)\nfunction verifyCloudflareIP(ip) {\n  // In a real implementation, check if IP is in Cloudflare's IP ranges\n  // https://www.cloudflare.com/ips/\n  return true // Replace with actual implementation\n}\n```\n\nThere's a third argument\nwhere you can use derive values from external plugin within key generator as well.\nOnly downsize is you have to definitely those types be yourself,\nplease be sure to test those values before actually defining types manually.\n\n```ts\nimport { ip } from 'elysia-ip'\n\nimport type { SocketAddress } from 'bun'\nimport type { Generator } from 'elysia-rate-limit'\n\nconst ipGenerator: Generator\u003c{ ip: SocketAddress }\u003e = (_req, _serv, { ip }) =\u003e {\n  return ip\n}\n```\n\n### countFailedRequest\n\n`boolean`\n\nDefault: `false`\n\nShould this plugin count rate-limit to user when request failed?\nBy default,\nthis plugin will refund request count to a client\nwhen `onError` lifecycle called.\n([Learn more in Lifecycle](https://elysiajs.com/concept/middleware.html#life-cycle))\n\n### context\n\n`Context`\n\nContext for storing requests count for each client, if you want to implement your own `Context` you can write it to comply with [`Context`](./src/@types/Context.ts) protocol\n\n```ts\nimport type { Context } from 'elysia-rate-limit'\n\nexport class CustomContext implements Context {\n  // implementation here\n}\n```\n\nBy default, context implementation, caching will be an LRU cache with a maximum of 5,000 entries. If you prefer to use this cache implementation but with larger cache size, you can define a new context with preferred cache size as follows\n\n```ts\nimport { DefaultContext } from 'elysia-rate-limit'\n\nnew Elysia().use(\n  rateLimit({\n    // define max cache size to 10,000\n    context: new DefaultContext(10_000),\n  })\n)\n```\n\n### headers\n\n`boolean`\n\nDefault `true`\n\nShould this plugin automatically set `RateLimit-*` headers to the response?\nIf you want to disable this feature, you can set this option to `false`.\n\n### skip\n\n`(request: Request, key: string): boolean | Promise\u003cboolean\u003e`\n\nDefault: `(): false`\n\nA custom function\nto determine that should this request be counted into rate-limit\nor not based on information given by `Request` object\n(i.e., Skip counting rate-limit on some route) and the key of the given request,\nby default, this will always return `false` which means counted everything.\n\n### injectServer\n\n`() =\u003e Server`\n\nDefault: `undefined`\n\nA function to inject server instance to the plugin,\nthis is useful\nwhen you want to use default key generator in detached Elysia instances.\nYou can check out the example [here](./example/multiInstanceInjected.ts).\n\nPlease use this function as a last resort,\nas defining this option will make plugin to make an extra function call,\nwhich may affect performance.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frayriffy%2Felysia-rate-limit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frayriffy%2Felysia-rate-limit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frayriffy%2Felysia-rate-limit/lists"}