Problem and Route Setup0:00
The topic for today's lesson comes courtesy of Sam, and Sam would like to know how to go about limiting the number of downloads he offers to a User each day. So let's imagine there's an endpoint to download some kind of file, whatever it is for your application. So I'm assuming this is a wildcard, but I'm going to hardcode it only because we have an example project here. But yeah, that will hit a DownloadsController and a method called show. So maybe this is something like one in our use case. All right. Well, at the very least, we want to restrict this route to authenticated users only, right?
Creating DownloadsController0:31
All right. Well, at the very least, we want to restrict this route to authenticated users only, right? We don't want guests downloading whatever one happens to be. All right. So quickly, we will create that controller, make me a controller called DownloadsController. All right. And if we quickly visit that, we'll add our show method to download whatever the item happens to be. In this case, it's not relevant, so I will return just a string here. Download the thing with an ID of one or, you know, whatever you captured in the wildcard.
Using Throttle Middleware1:32
Well, that may not be functionality that you want to offer. Maybe your subscription or your account only limits you to three downloads a day. Well, we could do this on the internet side. If you want to hard code it there, you could do it on the controller side, manually writing your guard logic, or we could also use Laravel's throttling middleware. And I think that's what we'll do. So have a look in the Kernel. If I scroll down, you'll see you do have a throttle route middleware, throttles requests, and it sounds like this is what we want. OK, so let's come back and we can append to it.
and it sounds like this is what we want. OK, so let's come back and we can append to it. So yes, you have to be signed in, but also we're going to throttle this. Now specifically, the authenticated User can hit this endpoint three times over the course of one minute, at least to start. Now if you're curious about the argument list here, of course, just go to the source. So if you take a look at the handle method, here's the closure, there's your first argument, the maximum number of attempts or page requests, and then the decay. So here we see the default. If I said throttle, that means you can hit this route 60 times each minute.
Testing Rate Limits2:38
So here we see the default. If I said throttle, that means you can hit this route 60 times each minute. OK, but let's bring it back to what I had before and give it a shot. So if I come back to Firefox and I give it a refresh, 1, 2, 3, but now if I do 4, no, 429. And again, remember, this is not application-wide. This is on a per-user basis. And really, that's all you have to do. Now of course, if you don't want to go the throttling route, you could manually perform this logic in your controller, and it's just going to be a mixture of caching.
Manual Caching Alternative3:07
Now of course, if you don't want to go the throttling route, you could manually perform this logic in your controller, and it's just going to be a mixture of caching. So for example, the user hits the endpoint, and you store it in the cache, and you associate it with their ID. So like users.{userId}.downloads. And every time they hit the endpoint, you increment the number of downloads. And then at some point, you check, well, is it more than three in this case? Then throw a 429 or redirect somewhere to their settings page to upgrade their account, something like that. So yeah, you could do it manually, but that's effectively what the throttling middleware
Dynamic Limits by Subscription4:10
So if we come back, 4, 5, 6, 7, 8, 9, 10, and there we go. Until tomorrow around this time, I will not be able to download anything. So one last piece of the puzzle, I mentioned that maybe your limit here could be contingent upon the User's subscription level. So for example, a basic subscription allows you 10 downloads per day, but a premium account allows you 100. So if you want to make this dynamic, we'd instead reference an attribute on User, something like this. Let's say you have a downloadLimit attribute. So when we provide a string here, Laravel will look on the User model.
Let's say you have a downloadLimit attribute. So when we provide a string here, Laravel will look on the User model. And I'll show you that real quick. On the middleware itself, it will figure out how many attempts are available. So here, yeah. So if the maximum attempts, that is currently set to downloadLimit. So if it's not a number, if it's a string, and we have a signed in User, try to access that as a property on the User model. Okay. So now this could be stored as a field in your database table, or we could just store
Okay. So now this could be stored as a field in your database table, or we could just store it as a property or an accessor. And in this case, you know, you perform whatever check you need to. You check the user's subscription level, and then return a number based on that. In this case, again, I will hard code it with the understanding that you would know how to determine that limit for your own project. So now if I come back, it's still not going to work because the user has downloaded 10 things. But if we cranked that up to 100, we can once again access it for another 85 times.
things. But if we cranked that up to 100, we can once again access it for another 85 times. All right. And that's all you need to know. So again, you could implement this logic yourself using caching, but why not instead use the rate limiting that Laravel provides you out of the box.
