Add Rate Limiting
Learn how to add rate limiting to protect your API routes
Learn how to use Plainform's built-in rate limiting to protect your API routes from abuse.
Goal
By the end of this recipe, you'll have rate limiting configured on your API routes.
Prerequisites
- A working Plainform installation
Plainform includes a custom rate limiting implementation in lib/rateLimit.ts. No external dependencies required.
Steps
Choose Rate Limiter
Plainform provides pre-configured rate limiters:
rateLimiters.strict- 5 requests per 10 seconds (sensitive operations)rateLimiters.standard- 10 requests per 10 seconds (general API routes)rateLimiters.lenient- 20 requests per 10 seconds (read-only operations)rateLimiters.email- 3 requests per 60 seconds (email sending)
Apply to API Route
Add rate limiting to your API route:
import { NextRequest, NextResponse } from 'next/server';
import {
rateLimiters,
getClientIdentifier,
createRateLimitResponse,
} from '@/lib/rateLimit';
export async function POST(req: NextRequest) {
// Get client identifier (IP address)
const identifier = getClientIdentifier(req);
// Check rate limit
const rateLimitResult = rateLimiters.standard(identifier);
if (!rateLimitResult.success) {
return createRateLimitResponse(rateLimitResult);
}
// Your API logic here
return NextResponse.json({ success: true });
}Test Rate Limiting
Test by making multiple requests quickly:
# Make 15 requests (should fail after 10 with standard limiter)
for i in {1..15}; do curl http://localhost:3000/api/your-route -X POST; doneThe response includes rate limit headers:
X-RateLimit-Limit- Maximum requests allowedX-RateLimit-Remaining- Requests remainingX-RateLimit-Reset- Timestamp when limit resetsRetry-After- Seconds to wait before retrying
Rate Limiter Examples
Strict (Payment Operations)
import { rateLimiters, getClientIdentifier, createRateLimitResponse } from '@/lib/rateLimit';
export async function POST(req: NextRequest) {
const identifier = getClientIdentifier(req);
const rateLimitResult = rateLimiters.strict(identifier);
if (!rateLimitResult.success) {
return createRateLimitResponse(rateLimitResult);
}
// Process payment
}Email (Newsletter Signup)
import { rateLimiters, getClientIdentifier, createRateLimitResponse } from '@/lib/rateLimit';
export async function POST(req: NextRequest) {
const identifier = getClientIdentifier(req);
const rateLimitResult = rateLimiters.email(identifier);
if (!rateLimitResult.success) {
return createRateLimitResponse(rateLimitResult);
}
// Send email
}Custom Rate Limiter
Create a custom rate limiter for specific needs:
import { createRateLimit } from '@/lib/rateLimit';
// 100 requests per hour
export const hourlyLimit = createRateLimit(100, 60 * 60 * 1000);
// 1000 requests per day
export const dailyLimit = createRateLimit(1000, 24 * 60 * 60 * 1000);Then use it in your API route:
import { hourlyLimit } from '@/lib/customRateLimit';
import { getClientIdentifier, createRateLimitResponse } from '@/lib/rateLimit';
export async function GET(req: NextRequest) {
const identifier = getClientIdentifier(req);
const rateLimitResult = hourlyLimit(identifier);
if (!rateLimitResult.success) {
return createRateLimitResponse(rateLimitResult);
}
// Your logic
}Per-User Rate Limiting
Rate limit by user ID instead of IP:
import { auth } from '@clerk/nextjs/server';
import { rateLimiters, createRateLimitResponse } from '@/lib/rateLimit';
export async function POST(req: NextRequest) {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
// Use userId as identifier
const rateLimitResult = rateLimiters.standard(userId);
if (!rateLimitResult.success) {
return createRateLimitResponse(rateLimitResult);
}
// Your API logic
}Production Considerations
The built-in rate limiter uses in-memory storage. For production with multiple servers, consider using Redis with Upstash or similar.
Upgrade to Redis
For production deployments with multiple servers:
- Install Upstash packages:
npm install @upstash/ratelimit @upstash/redis-
Replace the in-memory implementation in
lib/rateLimit.tswith Redis-backed storage -
See Upstash Rate Limiting docs for implementation details
Common Issues
Rate Limit Not Working
- Verify the rate limiter is called before your API logic
- Check that
getClientIdentifier()returns a valid identifier - Test with multiple requests to trigger the limit
All Requests Blocked
- Check rate limit configuration is not too strict
- Verify IP address extraction is working correctly
- Clear the in-memory store by restarting the dev server
Next Steps
- Server Actions - Use server actions for mutations
Related Documentation
- API Routes - Next.js API routes
How is this guide ?
Last updated on