00:08
That's where we decide how many requests we want our API to be able to serve to a user or access token in X amount of minutes.
00:16
And we might add this to our API for a number of reasons. One, if the requests that our users are sending are expensive, either in terms of our server's
00:24
operation or dollar signs, maybe it costs us some money to get this information and serve it to the user, then we're going to have a stricter limit on those than we would
00:33
say something that we can easily cache that's always going to be the same. That relatively is low cost, low impact on our server and wallet.
00:41
So for this, if we jump back into our text editor, within the previous series, we've already installed the AdonisJS rate limiting package.
00:49
We can find the configuration for it within our config folder. There's this limiter file right here. We're using our database to store those rate limits.
00:57
An alternative approach is to use Redis, but this is going to allow us to just open up TablePlus and inspect it nice and easily inside of the table. Currently, we are already using it.
01:06
If we jump into our off actions folder within HTTP on the web login to limit the amount
01:12
of attempts a user can make to log in for a particular email. So if you're curious on either how to get the rate limiter package installed and configured
01:21
or how to do this manual approach to the rate limiter, then I'll have those two lessons linked down below.
01:28
In this lesson, though, we're going to be able to make use of HTTP rate limits, which we defined similarly to middleware.
01:36
So if we collapse all of those folders back down, we can find these within our start directory inside of the limiter file. This throttle one is what the package came with whenever we configured it.
01:45
And let's go ahead and walk through what it's doing real quick. So it's called global. This is the key that's going to be used inside of the database anytime a request comes in.
01:54
And it's defining the limiter to allow 10 requests per minute. So we can define additional beyond this, but let's go ahead and inspect what this global looks like here.
02:04
So let's apply it to the lesson endpoint that we already have up and running within Hopscotch. So we can use it like a middleware.
02:10
So we use the use method here, and we can import the exported name of the limiter, which is throttle here and apply it as that middleware.
02:19
So if we import throttle from our start limiter file, if we give that a save, jump back over to Hopscotch. Now we're allowing 10 requests every minute.
02:27
So if I just sit here and spam this 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, that's the final allowed request.
02:37
If we hit 11, we're going to get back too many requests. Retry after 51, and I believe that's seconds.
02:45
So if we continue to try, you'll see we're still getting too many requests. Now it's 23 seconds because some time has passed since I last sent that.
02:54
If we send it off one more time, now we have 12 seconds left, I'll wait a little bit longer. And now 12 seconds should have passed if I send that off now. Now we're able to get data back again.
03:04
So it's limiting our users access to our API, and that could be to prevent spam. If somebody's just spamming our endpoint nonstop, that's expensive for us to do because it has
03:13
to go through our server in order to send that back. So there's going to be some form of limit there. But we also might have this behind a paywall.
03:21
So if we wanted to limit the amount of requests before they have to start paying, that's another use case for this here. So before we dig in and start creating our own rate limit to apply for our API, I'm going
03:31
to go ahead and jump into TablePlus here. You can just follow along on screen. And let's dig into our rate limits folder to see exactly what this looks like.
03:40
And you can see right now we have the rate limit prefix global, which is the rate limit name.
03:46
And then it's using my IP address to accumulate points against me whenever I send requests off.
03:53
And the colon colon one is my IP address value. Since I'm running through localhost, it's just going to be localhost there. Points are the amount of points.
04:03
So this is the X amount of requests per whatever the time limit is. This is where it's keeping track of that. And then the expiry timestamp for when those points should expire.
04:13
So if I jump back into Hopscotch, send a few requests off here, jump back into TablePlus, give it a refresh, I just sent off three requests. So now I have three points.
04:23
So that's how it's keeping track of the X amount in our request rate limit. And the key is who those points point toward.
04:31
By default, it's going to use the IP address since this is an HTTP rate limit, but we can change that.
04:37
So if we jump back into our text editor, let's go into our start limiter file. And we'll call this our export constant, I'm just going to call this API throttle,
04:46
we can do limiter dot define our rate limit, give it a name. So we can call this like global underscore API or something of a sort to find our callback function.
04:55
And this is where we actually build out the limiter. So let's return limiter dot allow requests within your production API, you're probably
05:02
going to want this to be a larger number so that your users can actually make use of your API. But for us so that we can keep things testable, let's go ahead and just do five there so that
05:12
we don't have to spend too much time sending off requests and spamming our own API. And let's say we'll allow five requests every 30 seconds so that I don't have to wait too
05:21
long in between our tests. How we have this currently is the exact same as our global rate limit. So it's going to use our IP address.
05:28
In order for us to change that, we can chain an additional method called using key. This is the key within the database that it will use to count the points against us as
05:38
we send requests off. So we can do backticks here, prefix this with something like global API to uniquely identify
05:44
this throttle so that we can add additional throttles to the user itself without incrementing their points as they hit those other throttles. We'll experiment with that here momentarily.
05:54
But what we need to do now is make this unique to the individual somehow who is sending the request.
06:01
And to do that within the callback function, we're provided our HTTP context. And with how our API is set up, what we really want to do is limit the access token against
06:11
our organization rather than the user's IP address so that the access token can send X amount of requests rather than the IP address can send X amount of requests.
06:20
So through our HTTP context, we can go ahead and do const and we can grab the organization's
06:25
ID via CTX and we can reach through our organization population off of the middleware, do dot and reach through to its ID value.
06:33
So we can do global API and I'm just going to abbreviate this org ID and then we can pass in the organization ID there.
06:41
If we give this a save, jump back over into our version one file for our route definitions. Let's leave the throttle on there. So let's wrap it in brackets so that it is an array.
06:51
And let's add our API throttle as well. Now both our throttle and API throttle are being applied to our lesson search endpoint.
07:00
So if we give this a save, jump back into Hopscotch, our API throttle has a more strict limitation. We're allowing five requests per 30 seconds.
07:08
So if we send one, two, three, four, five, we've reached the max.
07:14
So if we send one more, we're going to get back too many requests sent, retry after. And this is doing 25 seconds because our rate limit timestamp is allowing per 30.
07:24
So the retry after by default is going to go off of whatever X time it is that we provide into the rate limitation. I think I was talking there for longer than 25 seconds, so I'm going to go ahead and send
07:34
a few more off, rate limit ourselves again so that we can inspect this inside of the database. Okay. Let's jump back into TablePlus here, give it a refresh.
07:41
And now you'll see that we have two different points accumulating against our user. We have, if I expand this out, the global API one that we've defined, but we also have
07:51
that global one that we've initially defined as well. The global one has 11 points and our global API one has four points.
08:00
So because both of those rate limits are applied to this route definition, both are accumulating against the user just in different ways.
08:07
Our global is going against the IP address, whereas our global API is going against our organization's ID.
08:14
So if we jump back into Visual Studio Code and adjust these a little bit, if we switch this one from allowing 10 requests every one minute to instead allowing three requests
08:23
every one minute, you'll see that prior we were running into five as our limitation. But with this saved, if we just send off three now, we're going to hit our rate limit as
08:33
well. So if we send one, two, three, there's our max. If we send one more, now we're getting back the two minute requests and we're getting
08:40
56 as a retry after because that's per minute on that one. So that is how all of that works.
08:46
What we want to do is switch our API throttle here to be a broad limitation for our API.
08:52
This is the, we don't want users to spam rate limit that we're going to apply. So in a real world application, this is going to depend upon, again, the value of the data
09:02
that you're providing and the capabilities of your server. This might be something like 1000 requests every 30 seconds, or it might be something like 10 every 30 seconds.
09:11
Again, to keep this to where we can easily test it inside of Hopscotch, I'm going to go ahead and do 10 there so that we don't accidentally run into it, but we can still purposefully run into it.
09:21
So that should limit spam requests, and that's going against our organization there.
09:26
And we'll use this as our broad API throttle that will apply to all of our endpoints.
09:32
So rather than applying it to our lesson search, let's go ahead and get rid of it there. We can instead apply it into the array of the entire API version one group.
09:42
So API throttle, just like that. And I'm going to add it to the end. We definitely want it after our organization so that the CTX organization property is populated
09:51
and available for us to use inside of our throttle callback. Again, these are going to run in the order that we have them defined, so that is important.
09:58
So if we give this a save, now it's against any of our API endpoints. If we send off a get to our difficulties five times and then five for our access level,
10:08
the one more that we send beyond that for any of these other endpoints is going to hit our rate limit because all of these endpoints accumulate against the overall rate limit
10:16
score for the organization. So even though it's defined for the group here, if we jump back into Hopscotch and I
10:22
send off one, two, three for our search lessons, and then we jump over to our search courses, send off one, two, three, nope, we're getting back a validation error.
10:32
So I'm not entirely sure whether or not that counted. I guess we'll see.
10:35
Send off one, two, three for our get courses, and then if we send off one, two, now we have
10:43
reached our rate limitation, and because we didn't spam it, we no longer have to wait a full minute. We just have to wait that one second before we're able to send it off.
10:51
But you can kind of see how now that our rate limit is applied globally with our route group against our entire API endpoint availability, regardless of where we're sending the request,
11:01
it's still counting a score against our user. If you want to then add additional rate limitations, maybe our search are more expensive for our
11:09
server to do, so maybe we want to apply a more strict rate limitation to these as well.
11:14
We could do something like export const API search throttle equals limiter dot define
11:22
search API, or whatever you see fit there as the key, grab our CTX, const organization
11:27
ID equals CTX organization ID, and return limiter going to allow, maybe since these
11:35
are more expensive, we allow just five or three every 30 seconds. And we don't want to use the exact same key as our API throttle, as that's what distinguishes
11:45
inside of the database inside of the lookup to determine whether or not a user has sent too many requests between our different throttle rate limits.
11:53
So if we give this a copy, paste it down here, what we want to do is individualize it while still pointing against the organization ID.
12:01
So that's why I have appended on the rate limits name inside of our using key. Again, by default, if we weren't calling this using key, AdonisJS underneath the hood would
12:10
use the name value that we're defining the rate limiter with. But since we're defining a manual name, then we need to add that in here as well.
12:18
So we'll call this search API to individualize it from our API throttle.
12:23
And since these are more expensive, maybe we want to expand how long the user is restricted
12:29
from sending another request beyond the 30 second limitation that we have our rate limit defined for.
12:35
And for that, we can use the block for method to expand or otherwise define how long the user will be blocked for if they reach that rate limit.
12:44
So maybe we want to block them for a full hour if they hit this 30 second limitation. That's a bit harsh, but we can test it out and see exactly how that's going to limit
12:54
us by, let's jump into our route definitions here and apply it to both of our searches.
12:58
So if we use our API search throttle for both of these, I search throttle, since they're using the same rate limit, that's using the same key.
13:08
If we send three requests with our search courses and three with our search lessons, we're going to end up getting rate limited with our five limitation, despite them being
13:15
two separate endpoints, because they're using that same throttle and key inside of the database. So let's give that a save. Let's jump back into Hopscotch.
13:24
Let's go ahead and I'm just going to remove the request body so that our search courses is valid. Let's send off one, two, three, okay, all good.
13:32
Let's jump over now to our search lessons and send off one, two, three. Now we're blocked and you'll see that we're blocked now for 3600 seconds.
13:41
So we'd have to wait a full hour before we're able to send off another request. And you can see as I send these off, that's counting down.
13:48
So there's rate limiting in a nutshell for HTTP requests and how we can apply it to our
13:52
API to limit how often our users can access our API using their organization access token.