Reputation: 814
In my application, I would like to be able to catch the message that is being produced by the express-rate-limit package. This is an example of the code I have. I would like to be able to catch the message part with middleware so I can post-process it (in this case I have multiple languages ).
const apiCreatingAccountLimiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10 minutes
max: 10, // limit each IP to 10 requests per windowMs
message: {
limiter: true,
type: "error",
message: 'maximum_accounts'
}
});
and then
router.post('/signup', apiCreatingAccountLimiter, (req, res, next) => {
// handling the post request
})
I have a similar solution middleware setup for some of my other API messages:
// error processing middleware
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
res.status(statusCode).send({
type: 'error',
message: err.message,
fields: err.fields === '' ? '' : err.fields,
code: err.code === '' ? '' : err.code,
section: err.section === '' ? 'general' : err.section
});
});
However, when trying to read a message from the express-rate-limit package it does not seem to be passing via this middleware at all. I guess it's because it happens before it can even reach any API and trigger this middleware.
Looking at the res part passing through, I can see there is an object with the following data:
rateLimit:
{ limit: 10,
current: 10,
remaining: 0,
resetTime: 2019-10-21T12:35:46.919Z
},
But that does not seem to be transporting the message object that is set at the very top in the apiCreatingAccountLimiter. I wonder how could I get to it?
Does anyone know how this can be done? I do not want those messages to be translated on the front end. I need the translation to happen on the NodeJS server. I am only interested in the middleware part where I can catch the message and post-process it.
Upvotes: 6
Views: 11361
Reputation: 179
I found that my frontend was only able to catch the message if I set the statusCode to 200, even though technically it should be a 429. So try this instead:
const apiCreatingAccountLimiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10 minutes
max: 10, // limit each IP to 10 requests per windowMs,
statusCode: 200,
message: {
status: 429, // optional, of course
limiter: true,
type: "error",
message: 'maximum_accounts'
}
});
I did my best to match what you already had. Personally mine just looks like this basically:
const loginRatelimiter = rateLimit({
windowMs: 6 * 60 * 1000,
max: 10,
statusCode: 200,
message: {
status: 429,
error: 'You are doing that too much. Please try again in 10 minutes.'
}
})
Then, on my frontend I just check for res.data.error when the response comes in, and display that to the user if it exists.
Upvotes: 3
Reputation: 30975
In reading the source code, instead of using another middleware, you should play with the handler options as an option.
const apiCreatingAccountLimiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10 minutes
max: 10, // limit each IP to 10 requests per windowMs
message: "my initial message",
handler: function(req, res /*, next*/) {
var myCustomMessage = require('anotherModuleYouWannaUse_ForExemple');
res.status(options.statusCode).send(myCustomMessage);
},
});
At this end, you'll find an extract of the source code
function RateLimit(options) {
options = Object.assign(
{
windowMs: 60 * 1000, // milliseconds - how long to keep records of requests in memory
max: 5, // max number of recent connections during `window` milliseconds before sending a 429 response
message: "Too many requests, please try again later.",
statusCode: 429, // 429 status = Too Many Requests (RFC 6585)
headers: true, //Send custom rate limit header with limit and remaining
skipFailedRequests: false, // Do not count failed requests (status >= 400)
skipSuccessfulRequests: false, // Do not count successful requests (status < 400)
// allows to create custom keys (by default user IP is used)
keyGenerator: function(req /*, res*/) {
return req.ip;
},
skip: function(/*req, res*/) {
return false;
},
handler: function(req, res /*, next*/) {
res.status(options.statusCode).send(options.message);
},
onLimitReached: function(/*req, res, optionsUsed*/) {}
},
options
);
Upvotes: 8