Fastify Adapter
The Fastify adapter provides seamless integration with Fastify applications through a native plugin using the onRequest hook.
Installation
npm install @joint-ops/hitlimit fastify fastify-plugin pnpm add @joint-ops/hitlimit fastify fastify-plugin yarn add @joint-ops/hitlimit fastify fastify-plugin bun add @joint-ops/hitlimit fastify fastify-plugin Basic Usage
Register hitlimit as a Fastify plugin:
import Fastify from 'fastify'
import { hitlimit } from '@joint-ops/hitlimit/fastify'
const app = Fastify()
// Apply to all routes
await app.register(hitlimit, {
limit: 100,
window: '1m'
})
app.get('/', () => 'Hello')
await app.listen({ port: 3000 }) Route-Specific Rate Limiting
Use Fastify's encapsulated plugin system to apply different limits per route prefix:
// Strict limit on /api routes
app.register(async (scope) => {
await scope.register(hitlimit, {
limit: 100,
window: '1m'
})
scope.get('/users', handler)
}, { prefix: '/api' })
// Tight limit on auth routes
app.register(async (scope) => {
await scope.register(hitlimit, {
limit: 5,
window: '15m'
})
scope.post('/login', handler)
}, { prefix: '/auth' }) Custom Key Extraction
Rate limit by user ID, API key, or custom identifiers:
await app.register(hitlimit, {
limit: 100,
window: '1m',
key: (request) => {
// Rate limit by API key or fall back to IP
return request.headers['x-api-key'] || request.ip
}
}) Skipping Requests
Bypass rate limiting for certain requests:
await app.register(hitlimit, {
limit: 100,
window: '1m',
skip: (request) => {
// Skip health checks
return request.url === '/health'
}
}) Custom Error Response
Customize the response when rate limit is exceeded:
await app.register(hitlimit, {
limit: 100,
window: '1m',
response: (info) => ({
error: 'Rate limit exceeded',
retryIn: info.resetIn
})
}) Using with Stores
Use Redis or SQLite for distributed rate limiting:
import { hitlimit } from '@joint-ops/hitlimit/fastify'
import { redisStore } from '@joint-ops/hitlimit/stores/redis'
await app.register(hitlimit, {
limit: 100,
window: '1m',
store: redisStore({
url: 'redis://localhost:6379'
})
}) Migrating from @fastify/rate-limit
Switching from @fastify/rate-limit to hitlimit is straightforward. Both use Fastify's plugin system.
import rateLimit from '@fastify/rate-limit'
await app.register(rateLimit, {
max: 100,
timeWindow: '1 minute'
}) import { hitlimit } from '@joint-ops/hitlimit/fastify'
await app.register(hitlimit, {
limit: 100,
window: '1m'
}) | @fastify/rate-limit | hitlimit |
|---|---|
max: 100 | limit: 100 |
timeWindow: '1 minute' | window: '1m' |
keyGenerator: (req) => ... | key: (req) => ... |
errorResponseBuilder: (req, ctx) => ... | response: (info) => ... |
| No built-in tiered limits | tiers: { free: ..., pro: ... } |
| No built-in SQLite store | store: sqliteStore() |
See the full comparison for more details.
Advanced: Ban + Group Features
hitlimit supports auto-banning repeat offenders and shared rate limits across groups.
Auto-Ban After Threshold Violations
Automatically ban IPs that violate rate limits repeatedly:
await app.register(hitlimit, {
limit: 100,
window: '1m',
ban: {
threshold: 5, // Ban after 5 violations
duration: '15m' // Ban lasts 15 minutes
}
}) When a client exceeds the rate limit 5 times within the tracking window, they'll be banned for 15 minutes. During the ban, all requests return 429 immediately.
Shared Rate Limits via GroupId
Share rate limits across multiple clients using a group identifier:
await app.register(hitlimit, {
limit: 1000,
window: '1h',
group: (request) => {
// Share limit across user's team
return request.user?.teamId || request.user?.tier || 'free'
}
}) All requests with the same group ID share the same rate limit counter. Perfect for team-based limits in SaaS applications.
Next Steps
- Configuration Options - All available options
- Stores - Memory, SQLite, Redis
- Express Adapter - Express.js integration