Laravel: Using the Rate Limiter the easy way

One method magic

Italo Baeza Cabrera
3 min readAug 16, 2021
Photo by Jad Limcaco on Unsplash

The Rate Limiter should be one of the most overlooked features in Laravel. For those who are new to the Rate Limiter, the gist of it is simple to understand: to “rate limit” something based on the maximum number of attempts inside a window of time.

The normal and most common procedure to use the Rate Limiter is to, first, check if a key as been attempted too many times. If this key is available, only then we proceed to execute our logic. Finally, we increment the number of attempts along the window of time to “decay”.

This is basically a two-step logic.

The above translates in “If the foo key has been attempted more than 5 times, return something. Otherwise, execute the logic, and increment the foo attempts for the next 60 seconds”.

In other words, if the foo key is attempted more than 5 times in 60 seconds, the tooManyAttempts() will return true and nothing will be executed.

A simpler way to rate limit

The above works fine most of the time, but instead having to calling the Rate Limiter two times, with the same key, and leaving the logic between the two calls, we can do something more straightforward.

I like oneliners, and I just recently got approved a little helper called attempt(), that should land in Laravel 8.53. What it does is the same thing, but using a callback, and with the same default of 60 seconds. Our example above can be resumed in just a conditional statement:

Since it receives a Closure, the Rate Limiting can become portable. For example, you can save a function in the beginning and execute it later, instead of marrying the rate limiting to whatever you have between the check and the hit. As an alternative, you can always use a callable by using Closure::fromCallable().

The best implementation of the attempt() is for creating idempotent calls. For example, imagine we want to message an user, but skip the whole logic if the message is exactly the same he sent less than 2 minutes ago. That may happen if the connection is unstable, or when the user presses “Send” too many times accidentally.

No problem, we can get a hash of the message as the key and then check if it was sent previously.

The last cool thing about attempt() is that it will return the callback result. When the callback doesn’t return anything, or null, the attempt() will return true.

For example, if we want to return a model, no problem. If the callback wasn’t executed because it reached the rate limit, then false is always returned.

--

--

Italo Baeza Cabrera

Graphic Designer graduate. Full Stack Web Developer. Retired Tech & Gaming Editor. https://italobc.com