Performance Middleware
Rate Limiting
Protect your APIs from abuse. Configure limits per route, user, or IP. Simple setup, powerful protection.
Overview
Add rate limiting to any route with one line. Protect against abuse, ensure fair usage, and maintain performance.
1app.post('/users')
2 .rateLimit({ requests: 10, window: 60000 }) // 10 requests per minute
3 .handler(() => {
4 return createUser();
5 });Without rate limiting, your API is vulnerable to abuse, DDoS attacks, and unfair usage. With rate limiting, you control access and protect resources. Traditional rate limiting requires middleware setup, storage configuration, and manual tracking. We handle that automatically.
Without Rate Limiting
- Vulnerable to abuse and DDoS
- Unfair resource usage
- Manual tracking required
- Complex middleware setup
With MoroJS
- Automatic abuse protection
- Fair usage enforcement
- One-line setup
- Flexible per-route configuration
Configure limits per route. Different limits for different endpoints.
1// Authentication - strict limits
2app.post('/auth/login')
3 .rateLimit({ requests: 5, window: 900000 }) // 5 attempts per 15 minutes
4 .handler(() => authenticateUser());
5
6// Public API - higher limits
7app.get('/api/public')
8 .rateLimit({ requests: 1000, window: 3600000 }) // 1000 per hour
9 .handler(() => getPublicData());Protection
Protect against abuse, DDoS attacks, and resource exhaustion.
Fair Usage
Ensure fair resource distribution. Different limits for different users.
Simple
One line per route. Automatic tracking. Custom keys supported.
How It Works
MoroJS rate limiting tracks requests based on a key (IP, user ID, or custom) within a time window. When the limit is exceeded, requests are automatically rejected with a 429 status code and helpful headers.
Configuration
1import { createApp, z } from '@morojs/moro';
2
3const app = await createApp();
4
5// Rate limiting with chainable API
6app.post('/users')
7 .body(z.object({
8 name: z.string().min(2),
9 email: z.string().email()
10 }))
11 .rateLimit({ requests: 10, window: 60000 }) // 10 requests per minute
12 .handler((req) => {
13 return { success: true, data: createUser(req.body) };
14 });1// Authentication - strict limits
2app.post('/auth/login')
3 .rateLimit({ requests: 5, window: 900000 }) // 5 attempts per 15 minutes
4 .handler(() => authenticateUser());
5
6// Public API - higher limits
7app.get('/api/public')
8 .rateLimit({ requests: 1000, window: 3600000 }) // 1000 per hour
9 .handler(() => getPublicData());
10
11// File uploads - very strict
12app.post('/api/upload')
13 .rateLimit({ requests: 10, window: 3600000 }) // 10 uploads per hour
14 .handler(() => uploadFile());Advanced Configuration
Apply a limit to the whole app with the middleware form, and let successful traffic go uncounted where only failures should burn the budget.
1import { createApp, middleware } from '@morojs/moro';
2
3const app = await createApp();
4
5app.use(middleware.rateLimit({
6 requests: 100, // or max: 100
7 window: 60000, // or windowMs: 60000
8 message: 'Too many requests',
9 statusCode: 429 // default
10}));
11
12// Only count requests that failed — a login endpoint that
13// throttles brute force without punishing successful sign-ins
14app.post('/auth/login')
15 .rateLimit({
16 requests: 5,
17 window: 900000,
18 skipSuccessfulRequests: true
19 })
20 .handler(() => authenticateUser());1HTTP/1.1 429 Too Many Requests
2Retry-After: 47
3Content-Type: application/json
4
5{
6 "success": false,
7 "error": "Too many requests",
8 "retryAfter": 47
9}How limits are tracked
- Requests are keyed by client IP and route (METHOD:path) in an in-memory store
- The store is per process, so a clustered deployment limits per worker — size limits accordingly or rate limit at your proxy
- skipSuccessfulRequests counts up front and refunds once the response finishes below 400, so a burst cannot slip past mid-flight
- There is no keyGenerator, skip, or external store option