class TokenBucket {
constructor(capacity, refillRatePerSec) {
this.capacity = capacity;
this.refillRate = refillRatePerSec;
this.tokens = capacity;
this.lastRefill = Date.now();
}
refill() {
const now = Date.now();
const elapsedSec = (now - this.lastRefill) / 1000;
if (elapsedSec <= 0) return;
const added = elapsedSec * this.refillRate;
if (added > 0) {
this.tokens = Math.min(this.capacity, this.tokens + added);
this.lastRefill = now;
}
}
tryRemove(cost = 1) {
this.refill();
if (this.tokens >= cost) {
this.tokens -= cost;
return true;
}
return false;
}
retryAfter(cost = 1) {
this.refill();
const deficit = cost - this.tokens;
if (deficit <= 0) return 0;
return Math.ceil(deficit / this.refillRate);
}
isFull() {
this.refill();
return this.tokens >= this.capacity;
}
}
module.exports = TokenBucket;
const TokenBucket = require('./TokenBucket');
function rateLimiter(options = {}) {
const capacity = options.capacity || 60;
const refillRate = options.refillRate || 1;
const keyGenerator = options.keyGenerator || ((req) => req.ip);
const buckets = new Map();
const sweep = setInterval(() => {
for (const [key, bucket] of buckets) {
if (bucket.isFull()) buckets.delete(key);
}
}, 60000);
sweep.unref();
return function (req, res, next) {
const key = keyGenerator(req);
let bucket = buckets.get(key);
if (!bucket) {
bucket = new TokenBucket(capacity, refillRate);
buckets.set(key, bucket);
}
const allowed = bucket.tryRemove(1);
res.setHeader('X-RateLimit-Limit', capacity);
res.setHeader('X-RateLimit-Remaining', Math.max(0, Math.floor(bucket.tokens)));
if (!allowed) {
const retry = bucket.retryAfter(1);
res.setHeader('Retry-After', retry);
return res.status(429).json({
error: 'Too Many Requests',
retryAfter: retry
});
}
next();
};
}
module.exports = rateLimiter;
const express = require('express');
const rateLimiter = require('./rateLimiter');
const app = express();
app.set('trust proxy', 1);
app.use(express.json());
app.use(rateLimiter({ capacity: 100, refillRate: 10 }));
const loginLimiter = rateLimiter({
capacity: 5,
refillRate: 0.1,
keyGenerator: (req) => `${req.ip}:${req.body.email || 'anon'}`
});
app.post('/login', loginLimiter, (req, res) => {
res.json({ ok: true });
});
app.get('/health', (req, res) => res.json({ status: 'up' }));
app.listen(3000, () => {
console.log('listening on :3000');
});
This snippet implements per-client rate limiting using the token bucket algorithm, a classic approach that allows short bursts while capping the long-run request rate. A bucket holds up to capacity tokens and refills at a steady refillRate per second; each request costs one token, and when the bucket is empty the request is rejected. Unlike a fixed window counter, the token bucket smooths traffic and avoids the boundary spike where a client fires two full windows worth of requests across a window edge.
The TokenBucket class in the first tab is deliberately lazy: instead of running a timer to add tokens, refill() computes how many tokens should have accrued since lastRefill based on elapsed time. This means an idle bucket costs nothing until it is touched again, which matters when tracking thousands of clients. tryRemove() refills first, then removes a token only if one is available, returning a boolean the middleware uses to allow or deny. The helper retryAfter() reports how many seconds until the next token becomes available so callers can back off intelligently.
The rateLimiter middleware tab wires the bucket into Express. It keeps a Map of buckets keyed by a keyGenerator function, defaulting to the client IP, so each caller gets an independent quota. A periodic sweep in setInterval evicts buckets that are full and stale, preventing unbounded memory growth from one-off clients; the timer is unref()ed so it never keeps the process alive on its own. On each request the middleware sets the standard X-RateLimit-* headers, and on rejection it also sets Retry-After and responds 429.
The app.js tab shows the middleware applied globally with a tight limit and then overridden on a hot login route with a stricter bucket, demonstrating that limits compose per-router. Trade-offs are worth noting: this store is in-memory and per-process, so behind multiple instances each process enforces its own quota — a shared store like Redis is needed for a global limit. The IP default is also naive behind proxies, where req.ip should be trusted only when trust proxy is configured. Still, for a single node or as a coarse first line of defense, the in-memory token bucket is fast, dependency-free, and easy to reason about.
Related snips
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
export interface RetryOptions {
retries: number;
baseMs: number;
maxMs: number;
signal?: AbortSignal;
onRetry?: (attempt: number, delay: number, err: unknown) => void;
Exponential backoff with jitter for retries
module Api
module V1
class UsersController < BaseController
def show
user = User.includes(:profile).find(params[:id])
ETags for conditional requests and caching
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: String!
GraphQL API with Spring Boot
use crossbeam::channel::unbounded;
use std::thread;
fn main() {
let (tx, rx) = unbounded();
Crossbeam for advanced concurrent data structures
// Creating a Promise
const myPromise = new Promise((resolve, reject) => {
const success = true;
setTimeout(() => {
if (success) {
Promises and async/await patterns for asynchronous JavaScript
Share this code
Here's the card — post it anywhere.