throttle#
MongoDB-backed request limits per key, per minute and per day.
Requirements#
A MongoDB-compatible database.
Install#
npm i @nodedk/throttle
Usage#
var createThrottle = require('@nodedk/throttle')
module.exports = function createLimit({ db }) {
var throttle = createThrottle({ db })
return async function limit(state) {
var key = state.req.ip.split(',')[0].trim()
var result = await throttle.check(key, {
requests_per_minute: 10,
requests_per_day: 1000
})
if (result.allowed) return
state.res.statusCode = 429
return 'Too many requests'
}
}
db must provide MongoDB collection methods findOneAndUpdate() (returning
the updated document directly) and deleteMany().
The returned function can be called from the server’s before(state) hook, as in scaffold.
API#
createThrottle({ db })#
Returns { check, sweep } synchronously. db is required; all instances use its throttle collection.
throttle.check(key, limits = {})#
check(key, limits) returns a promise. It counts an attempt and returns { allowed: true } or
{ allowed: false, reason: 'RATE_LIMIT_DAY' } / RATE_LIMIT_MINUTE.
The key is a non-empty string identifying an account, IP address, or any group
sharing a limit. Include the site and endpoint when they need separate limits.
Instances using the same database and key share counters in the throttle
collection. Counter IDs use MongoDB's built-in unique _id index.
Limits must be non-negative safe integers. Omitted limits are unlimited; zero blocks all attempts. Windows reset at UTC minute and day boundaries. This is a fixed window limiter, so requests can burst on either side of a boundary.
The daily counter is checked first. Attempts rejected by the minute limit still count toward the daily limit. Attempts rejected by the daily limit do not increment the minute counter. The caller handles HTTP responses.
throttle.sweep()#
var result = await throttle.sweep()
console.log(result.n)
Call sweep() periodically to remove expired counters. Cleanup is explicit;
the library does not start timers or create TTL indexes. Expired counters do
not affect new windows even before cleanup.
Examples#
Let MongoDB remove expired counters automatically, as the scaffold does:
module.exports = async function prepare(db) {
await db
.collection('throttle')
.createIndex({ expires_at: 1 }, { expireAfterSeconds: 0 })
}
Created by Vidar Eldøy