{"id":18584510,"url":"https://github.com/bertrand31/api-rate-limiter","last_synced_at":"2025-05-16T05:33:14.812Z","repository":{"id":170071141,"uuid":"233895438","full_name":"Bertrand31/API-Rate-Limiter","owner":"Bertrand31","description":"A sample HTTP API using a custom rate limiter written in Scala","archived":false,"fork":false,"pushed_at":"2020-01-26T17:51:16.000Z","size":56,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-17T16:52:04.685Z","etag":null,"topics":["api","cats","functional-programming","http4s","rate-limiter","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Bertrand31.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2020-01-14T17:20:16.000Z","updated_at":"2024-09-06T17:03:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"8eb5b6ee-1f56-4f77-b355-d213c1728d6f","html_url":"https://github.com/Bertrand31/API-Rate-Limiter","commit_stats":null,"previous_names":["bertrand31/api-rate-limiter"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bertrand31%2FAPI-Rate-Limiter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bertrand31%2FAPI-Rate-Limiter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bertrand31%2FAPI-Rate-Limiter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bertrand31%2FAPI-Rate-Limiter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Bertrand31","download_url":"https://codeload.github.com/Bertrand31/API-Rate-Limiter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254473941,"owners_count":22077197,"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":["api","cats","functional-programming","http4s","rate-limiter","scala"],"created_at":"2024-11-07T00:27:45.777Z","updated_at":"2025-05-16T05:33:14.794Z","avatar_url":"https://github.com/Bertrand31.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# API Rate Limiter\n\n```\nMMMMMMMMMMMMMMMMMMMMMMMMWXOxoc;'..            ..';coxOXWMMMMMMMMMMMMMMMMMMMMMMMM\nMMMMMMMMMMMMMMMMMMMMN0dc,.                            .,cd0NMMMMMMMMMMMMMMMMMMMM\nMMMMMMMMMMMMMMMMWXkc'.         ..',;:cccccc:;,'..         .'ckXWMMMMMMMMMMMMMMMM\nMMMMMMMMMMMMMMNkc.       .':lxOKXNWMMMMMMMMMMWNXKOxl:'.       .ckNMMMMMMMMMMMMMM\nMMMMMMMMMMMWKo'      .'cx0NWMMMMMMMNkllookNMMMMMMMMMWN0xc'.      'oKWMMMMMMMMMMM\nMMMMMMMMMWKl.     .,o0NWWXNMMMMMMMM0,    ;KMMMMMMMMNXWMMMN0o,.     .l0WMMMMMMMMM\nMMMMMMMMXo.     .cONMW0o;.cKMMMMMMMK,    ;KMMMMMMMXl.,o0WMMMNOc.     .oXMMMMMMMM\nMMMMMMNk'     .oKWMMMNo.   ;0MMMMMM0,    ;KMMMMMMK:    lNMMMMMWKo.     'kNMMMMMM\nMMMMMXl.    .lKWMMMMMMNo.   ,OWMMMMNxllllkNMMMMM0,   .lXMMMMMMMMWKl.    .lXMMMMM\nMMMM0;     ,OWMMMMMMMMMNd. .;OWMMMMMMMMMMMMMMMMWO:. .oNMWXKNMMMMMMWO;     ;0MMMM\nMMMO,    .lXMMWXXWMMMMMMW0kKWMMMMMMMMMMMMMMMMMMMMWKkOWNOdkXWMMNKNMMMXl.    ,0MMM\nMM0,    .dNMMNo..cxKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKd:cOWWXkl,.:KMMMNd.    ,0MM\nMK;    .dNMMWd.    .;xNMMMMMMMMMMMMMMMMMMMMMMMMMMNOl''dXWOc.     :XMMMWd.    ;KM\nNl     lNMMMWXkc'.  .oNMMMMMMMMMMMMMMMMMMMMMMMWXx;..c0WMWO'   .:d0WMMMMNl     lN\nO.    ;XMMMMMMMMN0ocxNMMMMMMMMMMMMMMMMMMMMMMW0l.  ,kNMMMMW0llkXWMMMMMMMMX;    .O\nc    .kWMMMMMMMMMMMMMMMMMMMMMMMMMMWKkdd0XNXx;.  .oXMMMMMMMMMMMMMMMMMMMMMMk.    c\n.    :XMMMMMMMMMMMMMMMMMMMMMMMMMNx;..,oxdl'   .:0WMMMMMMMMMMMMMMMMMMMMMMMX:    .\n     oWMMWWWWWWWWMMMMMMMMMMMMMMXc..cdd:.     ,kNMMMMMMMMMMMMMMWWWWWWWWWMMWo\n    .xMMXl,,,,,,cKMMMMMMMMMMMMWo.;0WO,     .o00XMMMMMMMMMMMMMMO:,,,,,,dNMMk.\n    .kMM0'      .OMMMMMMMMMMMMNc .:kNKl. .:OO;,OMMMMMMMMMMMMMMx.      :NMMk.\n    .kMMXxcccccldXMMMMMMMMMMMMWk.   ,dKKO00o. cXMMMMMMMMMMMMMM0occccclkWMMk.\n    .xMMMMMMMMMMMMMMMMMMMMMMMMMWk'    'oxd, .lKMMMMMMMMMMMMMMMMMMMMMMMMMMMd.\n.    lNMMMMMMMMMMMMMMMMMMMMMMMMMMXxc,...'':o0WMMMMMMMMMMMMMMMMMMMMMMMMMMMNc    .\n,    '0MMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXXXXNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM0'    ,\nd.    ,llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll,    .d\nK;                                                                            ;K\nWk.                                                                          .kW\nMNc                                                                          cNM\n```\n\n- [Statement of purpose](#statement-of-purpose)\n- [API](#api)\n- [Rate limiting](#rate-limiting)\n- [Demonstration](#demonstration)\n- [Installation](#installation)\n  - [Scala and SBT](#scala-and-sbt)\n  - [Packaging for production](#packaging-for-production)\n\n## Statement of Purpose\n\nThis project aims to demonstrate how to implement an HTTP rate limiter in Scala.\nIt uses http4s for the web server, Circe for JSON handling, and is written in Scala 2.12.\n\n## API\n\nThis web serever binds itself to port 8080, and exposes two endpoints:\n\n- `/city/:city-name`: this one will return a list of the hotels of a given city ;\n- `/room/:room-name`: this one will return a list of hotels fulfilling to a room criteria.\n\nBoth endpoints allow the user to add an optional `price-sorting` parameter that takes either `ASC`\nor `DESC` and will sort the output hotels according to their prices and the order required.\n\nBoth endpoints are throttled. The first one will allow a maximum of 10 calls per 5 seconds, while\nthe second one allow 100 calls per 10 seconds.\n\nNote: all the parameters of both endpoints are case-insensitive.\n\n## Rate limiting\n\nThe main goal of this project is to implement and showcase a rate limiter in Scala.\nThe code for the rate limiter itself can be found here: [RateLimiter.scala](src/main/scala/ratelimiting/RateLimiter.scala).\n\nIt works as follows: first, we wrap a function with `wrapUnary`. If that function's type was\n`A =\u003e B`, the wrapped function has a type of `A =\u003e Option[B]`. This is because that wrapped function\nwill return a `None` if the rate limit has been reached and we're currently in the cooldown period.\nOtherwise, it will return a `Some` containing the output of the function.\nThus, the wrapped function is only called if the rate limit hasn't been reached, preventing any\nDOS attack.\n\n`wrapUnary` takes three arguments: the first is the unary function we want to throttle, the second\nis the rate limiting time window (we'll refer to it as `k`), and the third is the number of calls\nnot to exceed during the time window `k` (we will refer to that number as `n`).\n\nIf no value is provided for `k`, it will fall back to *10 seconds*. If no value is provided for `n`, it will fall back to *100*.\n\nInternally, the `wrapUnary` method maintains a mutable `Queue`.\nAt any given time, said queue will contain the timestamps corresponding to all the successful calls\nto the wrapped function within the last `k` seconds.\n\nEvery time the wrapped function is called, we first dequeue all timestemps older than `k` seconds.\nThen, we compare the length of the queue with `n`.\nIf the length of the queue is superior or equal to `n`, the rate limit has been reached within the\nlast `k` seconds ; `None` is returned.\n\nOtherwise, the current timestamp is added to said queue, the underlying function actually gets\nexecuted, and a `Some` is returned.\n\n## Demonstration\n\nThis project comes with a simple JS script to demonstrate the rate limiting of the city endpoint.\nTo run it, make sure you have NodeJS installed and run the following:\n```\n$\u003e node ./demonstrationScript.js\n```\nThis script was written in JavaScript and with no external dependencies so that it could easily be\nrun and modified without any other setup than a NodeJS install.\n\n## Installation\n\nThis project is built using SBT. As such, it can be run by using `sbt run`, and the specs can be\nrun using `sbt test`.\n\n### Scala and SBT\n\nPlease refer to the [SBT docs](https://www.scala-sbt.org/1.0/docs/Setup.html) for directions on\ninstallation.\n\n### Packaging for production\n\nFirst, clone the Harvester repo to a directory of your choice and `cd` into it.\nThen, in order to generate a binary, run the following:\n```\nsbt universal:packageBin\n```\nIt will generate a zip here:\n```\n./target/universal/rate-limiting-%VERSION_NUMBER%.zip\n```\nThis zip can now be shipped on a server, or used locally.\nOnce it is unzipped, the rate-limiter server can be started from inside the resulting folder with:\n```\n./bin/rate-limiting\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbertrand31%2Fapi-rate-limiter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbertrand31%2Fapi-rate-limiter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbertrand31%2Fapi-rate-limiter/lists"}