Rate limiting
A scraper hits your API at a hundred requests per second. Your database starts to struggle. Real users slow down. The scraper is not malicious, just enthusiastic, and it is ruining the experience for everyone. Rate limiting is the tool that stops this without asking you to identify the scraper first.
Where the limiter sits
The limiter runs before auth, before the handler, before the database. If the client is over the limit, the request is rejected with 429 and never touches the rest of the stack.
import rateLimit from 'express-rate-limit';
export const generalLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
limit: 100, // 100 requests per minute per IP
standardHeaders: 'draft-7',
legacyHeaders: false,
message: { error: 'Too many requests' },
});
// Stricter for auth so brute force has to crawl
export const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 10, // 10 login attempts per 15 minutes
message: { error: 'Too many auth attempts' },
});A strict limiter for auth endpoints and a looser one for everything else.
import { generalLimiter, authLimiter } from './middlewares/rate-limit';
app.use(generalLimiter);
app.post('/api/auth/login', authLimiter, loginHandler);
app.post('/api/auth/register', authLimiter, registerHandler);Apply the general limiter globally and the auth limiter on the sensitive endpoints.
The second limiter is much stricter than the first. Auth endpoints need it because they are the target of brute force attacks. Ten login attempts per fifteen minutes per IP is generous to real users and crippling to a password grinder. This single pattern prevents an enormous amount of credential abuse.
When the limit is hit, the library responds with 429 Too Many Requests and a Retry-After header that tells the client how long to wait. Real clients respect this and back off. Scrapers and attackers often do not, which is fine: the rate limit is still doing its job by shedding the load.
Quiz: Quiz
Loading practiceโฆ