Rate Limiting
Implement simple rate limiting using cache TTL.
Basic Pattern
import { MemoryCache } from '@humanspeak/memory-cache'
interface RateLimitEntry {
count: number
resetAt: number
}
const rateLimitCache = new MemoryCache<RateLimitEntry>({
maxSize: 100000,
ttl: 60 * 1000 // 1 minute window
})
function checkRateLimit(clientId: string, limit: number = 100): boolean {
const key = `ratelimit:${clientId}`
const entry = rateLimitCache.get(key)
if (!entry) {
// First request
rateLimitCache.set(key, {
count: 1,
resetAt: Date.now() + 60000
})
return true
}
if (entry.count >= limit) {
return false // Rate limit exceeded
}
// Increment counter
rateLimitCache.set(key, {
count: entry.count + 1,
resetAt: entry.resetAt
})
return true
}import { MemoryCache } from '@humanspeak/memory-cache'
interface RateLimitEntry {
count: number
resetAt: number
}
const rateLimitCache = new MemoryCache<RateLimitEntry>({
maxSize: 100000,
ttl: 60 * 1000 // 1 minute window
})
function checkRateLimit(clientId: string, limit: number = 100): boolean {
const key = `ratelimit:${clientId}`
const entry = rateLimitCache.get(key)
if (!entry) {
// First request
rateLimitCache.set(key, {
count: 1,
resetAt: Date.now() + 60000
})
return true
}
if (entry.count >= limit) {
return false // Rate limit exceeded
}
// Increment counter
rateLimitCache.set(key, {
count: entry.count + 1,
resetAt: entry.resetAt
})
return true
}Express Middleware
import { MemoryCache } from '@humanspeak/memory-cache'
import type { Request, Response, NextFunction } from 'express'
// Uses RateLimitEntry type and checkRateLimit function from Basic Pattern above
const rateLimitCache = new MemoryCache<RateLimitEntry>({
maxSize: 100000,
ttl: 60 * 1000
})
function rateLimitMiddleware(limit: number = 100) {
return (req: Request, res: Response, next: NextFunction) => {
const clientId = req.headers['x-client-id'] as string || req.ip
if (!checkRateLimit(clientId, limit)) {
return res.status(429).json({
error: 'Too Many Requests',
retryAfter: 60
})
}
next()
}
}
// Usage
app.use('/api', rateLimitMiddleware(100))import { MemoryCache } from '@humanspeak/memory-cache'
import type { Request, Response, NextFunction } from 'express'
// Uses RateLimitEntry type and checkRateLimit function from Basic Pattern above
const rateLimitCache = new MemoryCache<RateLimitEntry>({
maxSize: 100000,
ttl: 60 * 1000
})
function rateLimitMiddleware(limit: number = 100) {
return (req: Request, res: Response, next: NextFunction) => {
const clientId = req.headers['x-client-id'] as string || req.ip
if (!checkRateLimit(clientId, limit)) {
return res.status(429).json({
error: 'Too Many Requests',
retryAfter: 60
})
}
next()
}
}
// Usage
app.use('/api', rateLimitMiddleware(100))With Monitoring
import { MemoryCache } from '@humanspeak/memory-cache'
const rateLimitCache = new MemoryCache<RateLimitEntry>({
maxSize: 100000,
ttl: 60 * 1000,
hooks: {
onSet: ({ key, value }) => {
if (value.count === 1) {
metrics.increment('ratelimit.new_client')
}
},
onExpire: ({ key }) => {
metrics.increment('ratelimit.window_reset')
}
}
})
function checkRateLimit(clientId: string, limit: number): boolean {
const key = `ratelimit:${clientId}`
const entry = rateLimitCache.get(key)
if (!entry) {
rateLimitCache.set(key, { count: 1, resetAt: Date.now() + 60000 })
return true
}
if (entry.count >= limit) {
metrics.increment('ratelimit.exceeded', { clientId })
return false
}
rateLimitCache.set(key, {
count: entry.count + 1,
resetAt: entry.resetAt
})
return true
}import { MemoryCache } from '@humanspeak/memory-cache'
const rateLimitCache = new MemoryCache<RateLimitEntry>({
maxSize: 100000,
ttl: 60 * 1000,
hooks: {
onSet: ({ key, value }) => {
if (value.count === 1) {
metrics.increment('ratelimit.new_client')
}
},
onExpire: ({ key }) => {
metrics.increment('ratelimit.window_reset')
}
}
})
function checkRateLimit(clientId: string, limit: number): boolean {
const key = `ratelimit:${clientId}`
const entry = rateLimitCache.get(key)
if (!entry) {
rateLimitCache.set(key, { count: 1, resetAt: Date.now() + 60000 })
return true
}
if (entry.count >= limit) {
metrics.increment('ratelimit.exceeded', { clientId })
return false
}
rateLimitCache.set(key, {
count: entry.count + 1,
resetAt: entry.resetAt
})
return true
}Key Considerations
- Max Size: Plan for number of unique clients
- TTL: Determines the rate limit window
- Client ID: Use a reliable identifier (API key, IP, user ID)
- Distributed Systems: This pattern is per-instance; use Redis for distributed rate limiting