@fastify/rate-limit
Version:
A low overhead rate limiter for your routes
225 lines (192 loc) • 4.56 kB
JavaScript
'use strict'
const { test, mock } = require('node:test')
const Fastify = require('fastify')
const rateLimit = require('../index')
test('With global rate limit options', async t => {
t.plan(8)
const clock = mock.timers
clock.enable(0)
const fastify = Fastify()
await fastify.register(rateLimit, {
global: false,
max: 2,
timeWindow: 1000
})
const checkRateLimit = fastify.createRateLimit()
fastify.get('/', async (req, reply) => {
const limit = await checkRateLimit(req)
return limit
})
let res
res = await fastify.inject('/')
t.assert.deepStrictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), {
isAllowed: false,
key: '127.0.0.1',
max: 2,
timeWindow: 1000,
remaining: 1,
ttl: 1000,
ttlInSeconds: 1,
isExceeded: false,
isBanned: false
})
res = await fastify.inject('/')
t.assert.deepStrictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), {
isAllowed: false,
key: '127.0.0.1',
max: 2,
timeWindow: 1000,
remaining: 0,
ttl: 1000,
ttlInSeconds: 1,
isExceeded: false,
isBanned: false
})
res = await fastify.inject('/')
t.assert.deepStrictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), {
isAllowed: false,
key: '127.0.0.1',
max: 2,
timeWindow: 1000,
remaining: 0,
ttl: 1000,
ttlInSeconds: 1,
isExceeded: true,
isBanned: false
})
clock.tick(1100)
res = await fastify.inject('/')
t.assert.deepStrictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), {
isAllowed: false,
key: '127.0.0.1',
max: 2,
timeWindow: 1000,
remaining: 1,
ttl: 1000,
ttlInSeconds: 1,
isExceeded: false,
isBanned: false
})
clock.reset()
})
test('With custom rate limit options', async t => {
t.plan(10)
const clock = mock.timers
clock.enable(0)
const fastify = Fastify()
await fastify.register(rateLimit, {
global: false,
max: 5,
timeWindow: 1000
})
const checkRateLimit = fastify.createRateLimit({
max: 2,
timeWindow: 1000,
ban: 1
})
fastify.get('/', async (req, reply) => {
const limit = await checkRateLimit(req)
return limit
})
let res
res = await fastify.inject('/')
t.assert.deepStrictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), {
isAllowed: false,
key: '127.0.0.1',
max: 2,
timeWindow: 1000,
remaining: 1,
ttl: 1000,
ttlInSeconds: 1,
isExceeded: false,
isBanned: false
})
res = await fastify.inject('/')
t.assert.deepStrictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), {
isAllowed: false,
key: '127.0.0.1',
max: 2,
timeWindow: 1000,
remaining: 0,
ttl: 1000,
ttlInSeconds: 1,
isExceeded: false,
isBanned: false
})
// should be exceeded now
res = await fastify.inject('/')
t.assert.deepStrictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), {
isAllowed: false,
key: '127.0.0.1',
max: 2,
timeWindow: 1000,
remaining: 0,
ttl: 1000,
ttlInSeconds: 1,
isExceeded: true,
isBanned: false
})
// should be banned now
res = await fastify.inject('/')
t.assert.deepStrictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), {
isAllowed: false,
key: '127.0.0.1',
max: 2,
timeWindow: 1000,
remaining: 0,
ttl: 1000,
ttlInSeconds: 1,
isExceeded: true,
isBanned: true
})
clock.tick(1100)
res = await fastify.inject('/')
t.assert.deepStrictEqual(res.statusCode, 200)
t.assert.deepStrictEqual(res.json(), {
isAllowed: false,
key: '127.0.0.1',
max: 2,
timeWindow: 1000,
remaining: 1,
ttl: 1000,
ttlInSeconds: 1,
isExceeded: false,
isBanned: false
})
clock.reset()
})
test('With allow list', async t => {
t.plan(2)
const clock = mock.timers
clock.enable(0)
const fastify = Fastify()
await fastify.register(rateLimit, {
global: false,
max: 5,
timeWindow: 1000
})
const checkRateLimit = fastify.createRateLimit({
allowList: ['127.0.0.1'],
max: 2,
timeWindow: 1000
})
fastify.get('/', async (req, reply) => {
const limit = await checkRateLimit(req)
return limit
})
const res = await fastify.inject('/')
t.assert.deepStrictEqual(res.statusCode, 200)
// expect a different return type because isAllowed is true
t.assert.deepStrictEqual(res.json(), {
isAllowed: true,
key: '127.0.0.1'
})
})