This project demonstrates a simple rate-limiting mechanism for authenticated API requests using Ruby on Rails and Redis.
- Ruby (3.x)
- Rails (6 or 7)
- Redis (running locally)
- curl
This project includes a Development Container configuration, so you can get up and running instantly using Visual Studio Code.
- Visual Studio Code
- Dev Containers extension
- Docker installed and running
- Open the project folder in Visual Studio Code.
- When prompted, click “Reopen in Container”.
- VS Code will build the development environment using the included
.devcontainerconfiguration. - Once it's ready, you can start testing the api.
If you're not using the Dev Container, you can set up and run the API locally by executing the following command:
bin/setupLimit users to 3 requests per 30 seconds, using a sliding time window — meaning the limit is evaluated over a moving time frame, not fixed blocks.
I used Redis sorted sets (ZSET) to implement the sliding window logic. Here's the step-by-step breakdown of what happens when a request is made:
-
Store current timestamp Each time a user sends a request, the current time (as a float) is added to a sorted set:
$redis.zadd("rate_limit:user:#{user.id}", Time.now.to_f, Time.now.to_f)
-
Remove expired entries I remove all entries older than 30 seconds from the set:
$redis.zremrangebyscore("rate_limit:user:#{user.id}", 0, Time.now.to_f - 30)
-
Check how many requests remain I count how many entries are still in the set — this represents how many requests the user has made in the last 30 seconds:
request_count = $redis.zcard("rate_limit:user:#{user.id}")
-
Allow or block the request
-
If the count is 3 or fewer, the request is allowed.
-
If the count is more than 3, the request is blocked with:
-
HTTP status: 429 Too Many Requests
-
JSON error message: { "error": "Rate limit exceeded" }
Before testing rate limits, you need to create a user account.
curl --header "Content-Type: application/json" \
--data '{"user": {"username": "username", "password": "SecurePassword!1@"}}' \
http://localhost:3000/api/v1/signup -vUse your credentials to obtain a JWT token.
curl --header "Content-Type: application/json" \
--data '{"username": "username", "password": "SecurePassword!1@"}' \
http://localhost:3000/api/v1/authenticate -vSample response:
{
"token": "your.jwt.token.here"
}Copy this token for the next step.
First 3 Requests — Allowed
curl -H "Authorization: Bearer your jwt.token.here" \
http://localhost:3000/api/v1/rate_limit_testRepeat the request 3 times within 30 seconds.
Expected response:
{
"status": "allowed"
}4th Request — Blocked
Send a 4th request (within the same 30-second window):
curl -H "Authorization: Bearer your.jwt.token.here" \
http://localhost:3000/api/v1/rate_limit_testExpected response:
{ "error": "Rate limit exceeded" }HTTP status: 429 Too Many Requests