microlinkhq / async-ratelimiter

Rate limit made simple, easy, async.
MIT License
320 stars 23 forks source link

Add "decrease" parameter to allow reading the values without changing them #9

Closed nicokaiser closed 5 years ago

nicokaiser commented 6 years ago

In some scenarios it might be useful to be able to read the current "remaining" value for a limiter.

In this example, new login attempts are rejected when more at least 10 unsuccessful login attempts happened in the last 60 seconds.

const rateLimiter = new RateLimiter({
  db: new Redis()
  max: 10
  duration: 60 * 1000
})

const loginHandler = async (req, res, next) => {
  const limit = await rateLimiter.get({ id: req.clientIp, decrease: false })
  if (!limit.remaining) return sendError(req, res, 429)

  try {
    await doLogin(req);// or something like this
  } catch (err) {
    if (err) {
      await rateLimiter.get({ id: req.clientIp })
      return sendError(req, res, 401)
    }
  }

  next(req, res)
}

This is done with a new decrease parameter that controls if the remaining value should actually be decreased (default: true) or if the request is only done to read the current value.

What do you think?

coveralls commented 6 years ago

Pull Request Test Coverage Report for Build 40


Totals Coverage Status
Change from base Build 39: 0.7%
Covered Lines: 34
Relevant Lines: 34

💛 - Coveralls
nicokaiser commented 6 years ago

If you think this PR is useful, I can try to also implement this for tj/node-ratelimiter to make it consistent again.

Kikobeats commented 6 years ago

why not read the value directly from redis?

nicokaiser commented 6 years ago

why not read the value directly from redis?

That would be possible, but the "sliding window" effect would be gone then. When reading the value directly from Redis (with zcard), the count is only reset to 0 at duration after the last get call. When using get (or the PR implementation), the count is decreased duration after the first get call.

Kikobeats commented 5 years ago

Hello @nicokaiser, can you implement it at tj/node-ratelimiter? I interested in be 1:1 replacement

ghmeier commented 5 years ago

Any updates on this? It seems like essential functionality :)

Kikobeats commented 5 years ago

Sorry for the delay! Added into the codebase, also I'm going to add the example as part of the documentation.

Thanks for all 🙂

ghmeier commented 5 years ago

Oh yay! Thanks a bunch @Kikobeats!