A simple example of rate limiting API requests with Redis and Laravel

Hi fellows, I’ve been taking a course of Redis and I must say it’s quite awesome. There’s plenty of use cases in which Redis shines for solving them in an easy and simple way. For this post I will explain a simple example of how to rate limit the number of request an API can accept from a user. We are going to leverage from Laravel.

The first step is to create a command which allow us to establish the quota one user can consume on a daily basis. For this we are going to create the next command:

php artisan make:command ResetQuotaForFreeUsers

Once we have our command the logic is the next: The quota for free users is going to be 100 requests. You could set the quota higher for example 5k, this is just an example. So, if our application has 500 registered free users then the code will be:

class ResetQuotaForFreeUsers extends Command
{
/**
* The name and signature of the console command.
*
*
@var string
*/
protected $signature = 'reset:quotas';

/**
* The console command description.
*
*
@var string
*/
protected $description = 'This command resets the quota to free users.';

/**
* Create a new command instance.
*
*
@return void
*/
public function __construct()
{
parent::__construct();
}

/**
* Execute the console command.
*
*
@return mixed
*/
public function handle()
{
for ($i = 1; $i <= 500; $i++) {
Redis::command("hset", ["RateLimits", "User:{$i}:limit", 100]);
}
}
}

Now that we have established the quotas, we can start accepting requests.

The recently created command can be executed with the next line:

php artisan reset:quotas

Usually when you are consuming a third API you have to register, whether you have to pay or not for the service you will receive a pair of API keys which are formed by a Client Key and a Secret Key (you might only receive one key, it entirely depends on the service).

We are going to assume for this example that the User ID which is performing the request is the User 50. The logic behind how you find the correct user depends on if you are using a service like JWT, Laravel Passport, etc…

The second step is to create a middleware for validation, executing the next line:

php artisan make:middleware CheckRateLimit

Once the middleware is created, open the file /app/Http/Kernel.php. Then add the next line in the specified array:

protected $routeMiddleware = [
...
'rate.limit' => \App\Http\Middleware\CheckRateLimit::class,
];

So far you will have the middleware available for validation.

The logic in the middleware is simple, you need to check if the counter it’s still valid (greater than 0). We will accomplish this through the next validation in the middleware we just created:

class CheckRateLimit
{
/**
* Handle an incoming request.
*
*
@param \Illuminate\Http\Request $request
*
@param \Closure $next
*
@return mixed
*/
public function handle($request, Closure $next)
{
$id = 50;

if (Redis::command('hincrby', ['RateLimits', "User:{$id}:limit", -1]) >= 0) {
return $next($request);
} else {
return response()->json(['status' => 0, 'message' => 'You already reached your requests limit.']);
}
}
}

Now that you have set up the middleware, that last part is to perform requests to a specific route. I have created the next endpoint for testing and validation in the api routes file:

Route::get('/users', function () {
$users = factory('App\User', 5)->make();
return response()->json(['status' => 1, 'users' => $users], 200);
})->middleware('rate.limit');

As you can see we are making 5 users trough the factory helper, then we return them in a json response, the response is as follow:

{
"status": 1,
"users": [
{
"name": "Brett Torphy",
"email": "xander.konopelski@example.net"
},
{
"name": "Catherine Mraz",
"email": "rprice@example.com"
},
{
"name": "Madalyn Williamson",
"email": "ydubuque@example.net"
},
{
"name": "Dr. Hillard Barton",
"email": "schmeler.norma@example.com"
},
{
"name": "Zola Turner",
"email": "kristopher.jacobson@example.net"
}
]
}

But in case you exhaust all the limit of your requests, the response will be as follows:

{
“status”: 0,
“message”: “You already reached your requests limit.”
}

Now you are limiting the amount of requests a user can perform to your API, congrats!

Specifications to take into account:

If you have a suggestion or comments please let me know, I am learning too!

Like what you read? Give Hiram a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.