Understanding the Leaky Bucket Rate Limiter (with JavaScript Implementation)

I'm Shubham (@shubhamsinghbundela), I'm a Software Engineer, a Full-stack developer, a tech enthusiast, and a technical writer here on @Hashnode. I have a strong zeal to share my acquired knowledge and I am also willing to learn from others.
When building APIs or backend systems, one common challenge is handling too many requests at once. If a server receives a sudden spike of traffic, it may slow down or even crash.
To prevent this, systems use rate limiting algorithms.
One of the classic and widely used approaches is the Leaky Bucket Algorithm.
What is the Leaky Bucket Algorithm?
The Leaky Bucket Algorithm is a technique used to control the rate of requests sent to a system (like an API server). It ensures the system does not get overloaded by too many requests at once.
Idea in Simple Words
Imagine a bucket with a small hole at the bottom.
Water = incoming requests
Bucket = queue/buffer
Hole = fixed processing rate
Rules:
Requests enter the bucket.
The bucket leaks requests at a constant rate.
If the bucket becomes full, new requests are rejected.
So even if requests come very fast, they will leave at a steady rate.
Pizza Shop Example
Suppose you own a pizza shop.
Customers place orders very fast.
Your kitchen can only cook 1 pizza every 5 seconds.
So you create a waiting tray (bucket).
Tray capacity = 5 orders
Cooking rate = 1 pizza / 5 sec
Scenario:
1️⃣ 3 customers arrive → tray stores orders
2️⃣ Kitchen cooks 1 pizza every 5 sec
3️⃣ If tray becomes full (5 orders)
4️⃣ New customers are rejected
So the kitchen always works at constant speed, preventing overload.
Visual Representation
Requirements I implemented:
The bucket has a maximum capacity
Tasks are processed at a fixed interval (leak rate)
If the bucket is full, new tasks must be rejected immediately
Fairness must be preserved (FIFO execution)
Here’s the core implementation in JavaScript:
class LeakyBucket {
constructor(capacity, leakRateMs) {
this.capacity = capacity;
this.leakRateMs = leakRateMs;
this.bucket = []; // bucket capacity = this.capacity i.e. The bucket has a maximum capacity
this.timer = null;
}
add(task, onComplete) {
// If the bucket is full, new tasks must be rejected immediately
if(this.bucket.length===this.capacity){
task((err,data)=>{
err={}
err.message = "Rate Limit Exceeded"
onComplete(err);
})
}
// Lets fill bucket until buket full
if(this.bucket.length<this.capacity){
this.bucket.push({task, onComplete})
//Tasks are processed at a fixed interval (leak rate)
if (!this.timer) {
this.timer = setInterval(() => this._process(), this.leakRateMs);
}
}
}
_process() {
// console.log(this.bucket)
if (this.bucket.length === 0) {
clearInterval(this.timer);
this.timer = null;
return;
}
const {task, onComplete} = this.bucket?.shift();
console.log(task);
task((err, data)=>{
onComplete(err, data);
})
}
}
Where Is This Used?
The Leaky Bucket algorithm is commonly used in:
API rate limiting
Network traffic shaping
Distributed systems
Cloud infrastructure
It helps maintain system stability by smoothing traffic spikes.
Final Thoughts
The Leaky Bucket algorithm is simple but powerful.
It ensures that:
Servers are protected from traffic spikes
Requests are processed in a fair order
System performance remains stable
Understanding these algorithms helps




