{"id":13517869,"url":"https://github.com/valeriansaliou/node-fast-ratelimit","last_synced_at":"2025-04-07T13:06:06.431Z","repository":{"id":46318649,"uuid":"52077336","full_name":"valeriansaliou/node-fast-ratelimit","owner":"valeriansaliou","description":":umbrella: Fast and efficient in-memory rate-limit for Node, used to alleviate most common DOS attacks.","archived":false,"fork":false,"pushed_at":"2023-11-06T08:53:08.000Z","size":378,"stargazers_count":107,"open_issues_count":0,"forks_count":19,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-10-30T04:25:30.471Z","etag":null,"topics":["dos","nodejs","npm","npm-package","rate-limiter","security"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/fast-ratelimit","language":"JavaScript","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/valeriansaliou.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2016-02-19T09:36:46.000Z","updated_at":"2024-05-16T22:20:49.000Z","dependencies_parsed_at":"2023-02-08T07:16:36.857Z","dependency_job_id":"f9f6929a-1c22-43f2-ae57-09dd6e62190a","html_url":"https://github.com/valeriansaliou/node-fast-ratelimit","commit_stats":{"total_commits":105,"total_committers":4,"mean_commits":26.25,"dds":0.02857142857142858,"last_synced_commit":"cc769f1e7bb72113f502fc52815d02e9abf80cfd"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valeriansaliou%2Fnode-fast-ratelimit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valeriansaliou%2Fnode-fast-ratelimit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valeriansaliou%2Fnode-fast-ratelimit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valeriansaliou%2Fnode-fast-ratelimit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/valeriansaliou","download_url":"https://codeload.github.com/valeriansaliou/node-fast-ratelimit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247276098,"owners_count":20912287,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["dos","nodejs","npm","npm-package","rate-limiter","security"],"created_at":"2024-08-01T05:01:38.251Z","updated_at":"2025-04-07T13:06:06.411Z","avatar_url":"https://github.com/valeriansaliou.png","language":"JavaScript","funding_links":["https://www.buymeacoffee.com/valeriansaliou"],"categories":["JavaScript"],"sub_categories":[],"readme":"# node-fast-ratelimit\n\n[![Test and Build](https://github.com/valeriansaliou/node-fast-ratelimit/workflows/Test%20and%20Build/badge.svg?branch=master)](https://github.com/valeriansaliou/node-fast-ratelimit/actions?query=workflow%3A%22Test+and+Build%22) [![Build and Release](https://github.com/valeriansaliou/node-fast-ratelimit/workflows/Build%20and%20Release/badge.svg)](https://github.com/valeriansaliou/node-fast-ratelimit/actions?query=workflow%3A%22Build+and+Release%22) [![NPM](https://img.shields.io/npm/v/fast-ratelimit.svg)](https://www.npmjs.com/package/fast-ratelimit) [![Downloads](https://img.shields.io/npm/dt/fast-ratelimit.svg)](https://www.npmjs.com/package/fast-ratelimit) [![Gitter](https://img.shields.io/gitter/room/valeriansaliou/node-fast-ratelimit.svg)](https://gitter.im/valeriansaliou/node-fast-ratelimit) [![Buy Me A Coffee](https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg)](https://www.buymeacoffee.com/valeriansaliou)\n\nFast and efficient in-memory rate-limit, used to alleviate most common DOS attacks.\n\nThis rate-limiter was designed to be as generic as possible, usable in any NodeJS project environment, regardless of whether you're using a framework or just vanilla code. It does not require any dependencies, making it lightweight to install and use.\n\n**🇫🇷 Crafted in Lannion, France.**\n\n## Who uses it?\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd align=\"center\"\u003e\u003ca href=\"https://crisp.chat/\"\u003e\u003cimg src=\"https://valeriansaliou.github.io/node-fast-ratelimit/images/crisp.png\" width=\"64\" /\u003e\u003c/a\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ca href=\"https://www.doctrine.fr/\"\u003e\u003cimg src=\"https://valeriansaliou.github.io/node-fast-ratelimit/images/doctrine.png\" width=\"64\" /\u003e\u003c/a\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ca href=\"https://anchor.chat/\"\u003e\u003cimg src=\"https://valeriansaliou.github.io/node-fast-ratelimit/images/anchorchat.jpg\" width=\"64\" /\u003e\u003c/a\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ca href=\"https://westudents.it/\"\u003e\u003cimg src=\"https://valeriansaliou.github.io/node-fast-ratelimit/images/westudents.png\" width=\"64\" /\u003e\u003c/a\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd align=\"center\"\u003eCrisp\u003c/td\u003e\n\u003ctd align=\"center\"\u003eDoctrine\u003c/td\u003e\n\u003ctd align=\"center\"\u003eAnchor.Chat\u003c/td\u003e\n\u003ctd align=\"center\"\u003eWeStudents\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n_👋 You use fast-ratelimit and you want to be listed there? [Contact me](https://valeriansaliou.name/)._\n\n## How to install?\n\nInclude `fast-ratelimit` in your `package.json` dependencies.\n\nAlternatively, you can run `npm install fast-ratelimit --save`.\n\nTypeScript users can install type definitions by running `npm install --save-dev @types/fast-ratelimit`. _Note that this is a third-party package, ie. not maintained by myself._\n\n## How to use?\n\nThe `fast-ratelimit` API is pretty simple, here are some keywords used in the docs:\n\n * `ratelimiter`: ratelimiter instance, which plays the role of limits storage\n * `namespace`: the master ratelimit storage namespace (eg: set `namespace` to the user client IP, or user username)\n\nYou can create as many `ratelimiter` instances as you need in your application. This is great if you need to rate-limit IPs on specific zones (eg: for a chat application, you don't want the message send rate limit to affect the message composing notification rate limit).\n\nHere's how to proceed (we take the example of rate-limiting messages sending in a chat app):\n\n### 1. Create the rate-limiter\n\nThe rate-limiter can be instanciated as such:\n\n```javascript\nvar FastRateLimit = require(\"fast-ratelimit\").FastRateLimit;\n\nvar messageLimiter = new FastRateLimit({\n  threshold : 20, // available tokens over timespan\n  ttl       : 60  // time-to-live value of token bucket (in seconds)\n});\n```\n\nThis limiter will allow 20 messages to be sent every minute per namespace.\nAn user can send a maximum number of 20 messages in a 1 minute timespan, with a token counter reset every minute for a given namespace.\n\nThe reset scheduling is done per-namespace; eg: if namespace `user_1` sends 1 message at 11:00:32am, he will have 19 messages remaining from 11:00:32am to 11:01:32am. Hence, his limiter will reset at 11:01:32am, and won't scheduler any more reset until he consumes another token.\n\n### 2. Check by consuming a token\n\nOn the message send portion of our application code, we would add a call to the ratelimiter instance.\n\n#### 2.1. Consume token with asynchronous API (Promise catch/reject)\n\n```javascript\n// This would be dynamic in your application, based on user session data, or user IP\nnamespace = \"user_1\";\n\n// Check if user is allowed to send message\nmessageLimiter.consume(namespace)\n  .then(() =\u003e {\n    // Consumed a token\n    // Send message\n    message.send();\n  })\n  .catch(() =\u003e {\n    // No more token for namespace in current timespan\n    // Silently discard message\n  });\n```\n\n#### 2.2. Consume token with synchronous API (boolean test)\n\n```javascript\n// This would be dynamic in your application, based on user session data, or user IP\nnamespace = \"user_1\";\n\n// Check if user is allowed to send message\nif (messageLimiter.consumeSync(namespace) === true) {\n  // Consumed a token\n  // Send message\n  message.send();\n} else {\n  // consumeSync returned false since there is no more tokens available\n  // Silently discard message\n}\n```\n\n### 3. Check without consuming a token\n\nIn some instances, like password brute forcing prevention, you may want to check without consuming a token and consume only when password validation fails.\n\n#### 3.1. Check whether there are remaining tokens with asynchronous API (Promise catch/reject)\n\n```javascript\nlimiter.hasToken(request.ip).then(() =\u003e {\n  return authenticate(request.login, request.password)\n})\n  .then(\n    () =\u003e {\n      // User is authenticated\n    },\n\n    () =\u003e {\n      // User is not authenticated\n      // Consume a token and reject promise\n      return limiter.consume(request.ip)\n        .then(() =\u003e Promise.reject())\n    }\n  )\n  .catch(() =\u003e {\n    // Either invalid authentication or too many invalid login\n    return response.unauthorized();\n  })\n```\n\n#### 3.2. Check whether there are remaining tokens with synchronous API (boolean test)\n\n```javascript\nif (!limiter.hasTokenSync(request.ip)) {\n  throw new Error(\"Too many invalid login\");\n}\n\nconst is_authenticated = authenticateSync(request.login, request.password);\n\nif (!is_authenticated) {\n  limiter.consumeSync(request.ip);\n\n  throw new Error(\"Invalid login/password\");\n}\n```\n\n## Notes on performance\n\nThis module is used extensively on edge WebSocket servers, handling thousands of connections every second with multiple rate limit lists on the top of each other. Everything works smoothly, I/O doesn't block and RAM didn't move that much with the rate-limiting module enabled.\n\nOn one core of a 2,3 GHz 8-Core Intel Core i9, the parallel asynchronous processing of 100,000 namespaces in the same limiter take an average of 160 ms, which is fine (1.6 microseconds per operation).\n\n## Why not using existing similar modules?\n\nI was looking for an efficient, yet simple, DOS-prevention technique that wouldn't hurt performance and consume tons of memory. All proper modules I found were relying on Redis as the keystore for limits, which is definitely not great if you want to keep away from DOS attacks: using such a module under DOS conditions would subsequently DOS Redis since 1 (or more) Redis queries are made per limit check (1 attacker request = 1 limit check). Attacks should definitely not be allieviated this way, although a Redis-based solution would be perfect to limit abusing users.\n\nThis module keeps all limits in-memory, which is much better for our attack-prevention concern. The only downside: since the limits database isn't shared, limits are per-process. This means that you should only use this module to prevent hard-attacks at any level of your infrastructure. This works pretty well for micro-service infrastructures, which is what we're using it in.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvaleriansaliou%2Fnode-fast-ratelimit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvaleriansaliou%2Fnode-fast-ratelimit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvaleriansaliou%2Fnode-fast-ratelimit/lists"}