javascript
56 lines · 7 steps
How token-bucket rate limiting works in Express
A per-user token bucket refills over time and rejects requests once a user runs out of tokens.
Explained by
highlit
1class TokenBucket {
2 constructor({ capacity, refillPerSecond }) {
3 this.capacity = capacity;
4 this.refillPerSecond = refillPerSecond;
5 this.tokens = capacity;
6 this.lastRefill = Date.now();
7 }
8
9 refill() {
10 const now = Date.now();
11 const elapsedSeconds = (now - this.lastRefill) / 1000;
12 if (elapsedSeconds <= 0) return;
13 this.tokens = Math.min(this.capacity, this.tokens + elapsedSeconds * this.refillPerSecond);
14 this.lastRefill = now;
15 }
16
17 tryConsume(cost = 1) {
18 this.refill();
19 if (this.tokens >= cost) {
20 this.tokens -= cost;
21 return true;
22 }
23 return false;
24 }
25}
26
27class RateLimiter {
28 constructor(options) {
29 this.options = options;
30 this.buckets = new Map();
31 }
32
33 bucketFor(userId) {
34 let bucket = this.buckets.get(userId);
35 if (!bucket) {
36 bucket = new TokenBucket(this.options);
37 this.buckets.set(userId, bucket);
38 }
39 return bucket;
40 }
41
42 allow(userId, cost = 1) {
43 return this.bucketFor(userId).tryConsume(cost);
44 }
45}
46
47const limiter = new RateLimiter({ capacity: 10, refillPerSecond: 2 });
48
49export function rateLimit(req, res, next) {
50 const userId = req.user?.id ?? req.ip;
51 if (!limiter.allow(userId)) {
52 res.set('Retry-After', '1');
53 return res.status(429).json({ error: 'Too many requests' });
54 }
55 next();
56}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1The token bucket smooths bursts by allowing up to capacity requests instantly, then throttling to the refill rate.
- 2Computing refill lazily from elapsed time avoids running any background timer.
- 3Keying buckets by user in a Map isolates each caller's quota independently.
Related explainers
java
public class SlidingLogRateLimiter { private final int maxRequests; private final long windowMillis;
How a sliding-log rate limiter works
rate-limiting
concurrency
sliding-window
Advanced
8 steps
javascript
const express = require('express'); const router = express.Router(); const { pool } = require('../db'); const redis = require('../redis');
Building a health check endpoint in Express
health-check
timeouts
promise-race
Intermediate
9 steps
php
<?php namespace App\Http\Middleware;
Idempotent requests with Laravel middleware
idempotency
middleware
caching
Advanced
8 steps
javascript
'use client'; import { useEffect } from 'react'; import * as Sentry from '@sentry/nextjs';
How a Next.js error boundary recovers
error-boundary
error-handling
observability
Intermediate
8 steps
go
package middleware import ( "net/http"
A maintenance-mode gate in Gin
middleware
atomic-flag
concurrency
Intermediate
8 steps
javascript
const MAX_BATCH_SIZE = 20; const FLUSH_INTERVAL = 5000; const ENDPOINT = '/api/analytics';
Batching analytics events in the browser
batching
buffering
sendbeacon
Intermediate
10 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/how-token-bucket-rate-limiting-works-in-express-explained-javascript-0799/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.