DEV Community

Hiram
Hiram

Posted on

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
Enter fullscreen mode Exit fullscreen mode

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]);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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,
];
Enter fullscreen mode Exit fullscreen mode

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.']);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

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"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

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."
}
Enter fullscreen mode Exit fullscreen mode

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

Specifications to take into account:

You will need to run a redis server, redis quickstart documentation: https://redis.io/topics/quickstart
This example is using hashes in redis, hashes are like arrays in PHP, documentation about data types on redis can be found here: https://redis.io/topics/data-types
You need to install predis library for using redis along PHP, you can find it on packagist: https://packagist.org/packages/predis/predis

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

This post was originally published on medium: https://medium.com/@eichgi/a-simple-example-of-rate-limiting-api-requests-with-redis-d7aabd8c94fb

Top comments (0)