Many times, it is required to limit an API call to put some restriction on consumption of a particular resource.
A nice example would be rate limiting sending emails or sending OTP.
Let's say some malicious user ๐ฑโ๐ป is running a cURL
script to generate huge number of OTP in short time span.
Definitely, this case should be handled properly, otherwise we will end up paying a lot of money to the OTP service provider. ๐ค
So, in this article I will walk you through how we can have this kind of restriction on some API.
I will use /otp/send
as an example and rate limit on phone
number but It can be IP
of the client as well.
So let's say we have some API,
POST otp/send
{
"phone": "+918778XXXX78"
}
and we want to restrict it so that a user can only call it 5 times in 10 second of time window.
If you are familiar with express, the API would look like something below,
router.post('/otp/send', sendOTP);
So, it would be nice If we could create a middleware that contains all the logic to decide whether the request to send OTP should proceed or should drop.
Let's create that middleware,
const redis = require('async-redis');
const client = redis.createClient();
module.exports = async (req, res, next) => {
const { phone } = req.body; // could be ip as well, ip = req.ip;
const keyName = phone;
const current = await client.get(phone);
if (current && current > 5) {
throw new Error('Too many requests, please try after sometime!');
} else {
await client.multi()
.incr(keyName)
.expire(keyName, 10) // number of seconds
.exec();
next();
}
};
Finally, to use above middleware we simply need to attach this to the route,
router.post('/otp/send', rateLimitSendOTP, sendOTP);
This is what happening here,
- Whenever a user makes first time API call, a key will be set in Redis with expiration of 10 seconds and with counter value 1
- On next call, if the key isn't expired it will increment the counter by 1
- If key exist and the value of the counter is greater than 5 it will throw and error
- After 10 second key will be deleted and the cycle will start again
There are other rate limiter patterns in Redis, I have used pattern 1 here, I highly recommended to go through the doc at least for once.
Links:
Happy Learning! ๐