Rate Limiting Guide¶
This guide explains how to configure and use rate limiting in the Jodit AI Adapter service.
Overview¶
The rate limiter protects your service from abuse by limiting the number of requests a user or IP address can make within a time window. It supports two backends:
- In-Memory: Fast, simple, suitable for single-instance deployments
- Redis: Distributed, suitable for multi-instance deployments
Quick Start¶
In-Memory Rate Limiting¶
Perfect for development and single-instance production deployments:
RATE_LIMIT_ENABLED=true
RATE_LIMIT_TYPE=memory
RATE_LIMIT_MAX_REQUESTS=100
RATE_LIMIT_WINDOW_MS=60000
This allows 100 requests per minute per user/IP.
Redis Rate Limiting¶
For production deployments with multiple instances:
- Start Redis (using Docker Compose):
- Configure environment variables:
RATE_LIMIT_ENABLED=true
RATE_LIMIT_TYPE=redis
RATE_LIMIT_MAX_REQUESTS=100
RATE_LIMIT_WINDOW_MS=60000
REDIS_URL=redis://localhost:6379
REDIS_PASSWORD=your-password # optional
REDIS_DB=0 # optional
Configuration Options¶
| Variable | Description | Default |
|---|---|---|
RATE_LIMIT_ENABLED |
Enable/disable rate limiting | false |
RATE_LIMIT_TYPE |
Type: memory or redis |
memory |
RATE_LIMIT_MAX_REQUESTS |
Max requests per window | 100 |
RATE_LIMIT_WINDOW_MS |
Time window in milliseconds | 60000 (1 minute) |
REDIS_URL |
Redis connection URL | - |
REDIS_PASSWORD |
Redis password | - |
REDIS_DB |
Redis database number | 0 |
RATE_LIMIT_KEY_PREFIX |
Key prefix in storage | rl: |
Programmatic Usage¶
import { start } from 'jodit-ai-adapter';
await start({
port: 8082,
rateLimit: {
enabled: true,
type: 'redis',
maxRequests: 100,
windowMs: 60000,
redisUrl: 'redis://localhost:6379',
keyPrefix: 'rl:'
},
providers: {
openai: {
type: 'openai',
apiKey: process.env.OPENAI_API_KEY
}
}
});
How It Works¶
Key Extraction¶
By default, rate limiting uses:
1. User ID (if authenticated via checkAuthentication callback)
2. IP Address (fallback if no user ID)
This means: - Authenticated users are tracked by their user ID across all IP addresses - Anonymous requests are tracked by IP address
Response Headers¶
When rate limiting is enabled, responses include:
When rate limit is exceeded:
Response Format¶
When rate limited, the API returns:
{
"success": false,
"error": {
"code": 429,
"message": "Too many requests, please try again later",
"details": {
"limit": 100,
"current": 101,
"resetTime": 45000
}
}
}
Advanced Configuration¶
Custom Key Extraction¶
You can customize how users/IPs are tracked:
import { createRateLimitMiddleware, MemoryRateLimiter } from 'jodit-ai-adapter';
const rateLimiter = new MemoryRateLimiter({
maxRequests: 100,
windowMs: 60000
});
const middleware = createRateLimitMiddleware(rateLimiter, {
keyExtractor: (req) => {
// Use API key from header
return req.headers['x-api-key'] as string;
}
});
Skip Certain Requests¶
Skip rate limiting for specific users or paths:
const rateLimiter = new MemoryRateLimiter({
maxRequests: 100,
windowMs: 60000,
skip: (key) => {
// Skip rate limiting for admin users
return key.startsWith('user:admin-');
}
});
Or in middleware:
const middleware = createRateLimitMiddleware(rateLimiter, {
skip: (req) => {
// Skip health check endpoint
return req.path === '/health';
}
});
Custom Error Messages¶
const middleware = createRateLimitMiddleware(rateLimiter, {
message: 'Slow down there, partner! Try again in a minute.'
});
On Limit Reached Callback¶
Track when users hit the rate limit:
const middleware = createRateLimitMiddleware(rateLimiter, {
onLimitReached: async (req, key) => {
console.log(`Rate limit exceeded for ${key}`);
// Log to monitoring service
await monitoring.trackRateLimitExceeded(key);
}
});
Docker Deployment¶
Development¶
# Start Redis with monitoring UI
docker-compose -f docker-compose.dev.yml up -d
# Access Redis Commander at http://localhost:8081
Production¶
version: '3.8'
services:
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
command: redis-server --appendonly yes
app:
build: .
environment:
- RATE_LIMIT_ENABLED=true
- RATE_LIMIT_TYPE=redis
- REDIS_URL=redis://redis:6379
depends_on:
- redis
Testing¶
Run rate limiter tests:
# In-memory tests
npm test -- src/rate-limiter/memory-rate-limiter.test.ts
# Redis tests (requires Redis running)
REDIS_URL=redis://localhost:6379 npm test -- src/rate-limiter/redis-rate-limiter.test.ts
# Middleware integration tests
npm test -- src/middlewares/rate-limit.test.ts
Monitoring¶
Redis Commander¶
When using docker-compose.dev.yml, you get Redis Commander UI:
Check Rate Limit Status¶
const state = await rateLimiter.getState('user:123');
console.log({
current: state.current, // Current request count
remaining: state.remaining, // Remaining requests
allowed: state.allowed, // Whether next request will be allowed
resetTime: state.resetTime // MS until reset
});
Troubleshooting¶
Rate limiting not working¶
- Check
RATE_LIMIT_ENABLED=trueis set - Verify configuration is loaded correctly
- Check logs for rate limiter initialization messages
Redis connection errors¶
- Verify Redis is running:
redis-cli ping - Check
REDIS_URLis correct - Check network connectivity
- Verify password if using authentication
Different users sharing limits¶
This happens when:
- No authentication is implemented (all users tracked by IP)
- Multiple users behind same proxy/NAT
- Solution: Implement checkAuthentication callback
Rate limiter fails open¶
By design, if Redis is unavailable, requests are allowed through. This prevents Redis downtime from taking down your service. Monitor Redis health separately.
Best Practices¶
- Start conservative: Begin with stricter limits (e.g., 50 req/min) and adjust based on usage
- Monitor usage: Track rate limit hits to tune limits appropriately
- Use Redis in production: For consistency across multiple instances
- Set up monitoring: Alert when rate limits are frequently hit
- Document limits: Make users aware of rate limits in API documentation
- Provide feedback: Use descriptive error messages and Retry-After headers
- Different limits for different tiers: Consider implementing tiered rate limiting
Examples¶
Basic Setup¶
Aggressive Limiting¶
Lenient Limiting¶
Performance¶
In-Memory¶
- Very fast (~1μs per request)
- Cleanup runs every 60 seconds
- Memory usage: ~100 bytes per tracked key
Redis¶
- Fast (~1ms per request)
- Automatic expiry (no cleanup needed)
- Scales horizontally
- Adds network latency
Security Considerations¶
- Rate limiting is not a security feature by itself
- Use in combination with authentication
- Don't rely solely on IP-based limiting (can be spoofed behind proxies)
- Implement proper authentication with
checkAuthentication - Consider additional security measures (firewall rules, WAF, etc.)