Masonite includes a rate limiting feature which make it really easy to limit an action during a given time window.
The most frequent use of this feature is to add throttling to some API endpoints but you could also use this to limit how many time a model can be updated, a job can be run in the queue or a mail should be sent.
This feature is based on the application's Cache features. It will store the number of attempts of a given key
and will also store the associated time window.
Rate Limiting feature can be accessed via the container application.make("rate")
or by using the RateLimiter
facade.
To limit an action, we need:
a key
that will uniquely identify the action
a number of authorized attempts
a delay after which number of attempts will be reset
Before we can start using the rate limiting features of Masonite, we need to register the RateProvider
class in our providers list:
You can add throttling to HTTP requests easily by using the ThrottleRequestsMiddleware
.
First register the middleware as a route middleware into your project:
This middleware is taking one argument which can be either a limit string or a limiter name.
By using limit strings such as 100/day
you will be able to add a global limit which does not link this limit to a given user, view or IP address. It's really an absolute limit that you will define on your HTTP requests.
Units available are: minute
, hour
and day
.
We now just have to use it in our routes:
To be able to add throttling per users or to implement more complex logic you should use Limiter
. Limiter
are simple classes with a allow(request)
method that will be called each time a throttled HTTP request is made.
Some handy limiters are bundled into Masonite:
GlobalLimiter
UnlimitedLimiter
GuestsOnlyLimiter
But you can create your own limiter:
Here we are creating a limiter authorizing 2 requests/day for guests users, 10 requests/day for authenticated non-premium users and unlimited requests for premium users. Here we are using by(key)
to define how we should identify users. (TODO: explain more)
Finally you can register your limiter(s) in your application provider:
We now just have to use it in our routes:
Now when making unauthenticated requests to our /api
endpoints we will see some new headers in the response:
X-Rate-Limit-Limit
: 5
X-Rate-Limit-Remaining
: 4
After reaching the limit two headers are added in the response, X-Rate-Limit-Reset
which is the timestamp in seconds defining when rate limit will be reset and when api endpoint will be available again and Retry-After
which is the number of seconds in which rate limit will be reset:
X-Rate-Limit-Limit
: 5
X-Rate-Limit-Remaining
: 0
X-Rate-Limit-Reset
: 1646998321
Retry-After
: 500
A ThrottleRequestsException
exception is raised and a response with status code 429: Too Many Requests
and content Too many attempts
is returned.
The response can be customized to provide different status code, content and headers. It can be done by adding a get_response()
method to the limiter.
In the previous example it would look like this:
To use Rate Limiting feature we should define limits. A limit is a number of attempts during a given time frame. It could be 100 times per day or 5 times per hour. In Masonite limits are abstracted through the Limit
class.
Here is an overview of the different ways to create limits:
Then to associate a given key to this limit we can do:
This feature allows to simply limit calling a Python callable. Here we will limit the given callable to be called 3 times per hour for the key sam
.
Let's make one attempt:
Alternatively you can manually incrementing attempts:
We can get the number of attempts:
We can get the number of remaining attempts:
We can check if too many attempts have been made:
We can reset the number of attempts:
We can get the seconds in which will be able to attempt the action again:
We can get the UNIX timestamps in seconds when will be able to attempt the action again:
Here is a complete use case, that will determine if an email should be send to the given user: