Fixed window
One counter per clock-aligned window.
0 of 5 slots used in this window. They all empty at the boundary.
- Allowed
- 0
- Rejected
- 0
- Room
- 5/5
- Next
- now
Step by step
Send a request to watch this algorithm decide.
An interactive guide to the three algorithms in elysia-rate-limiter
View sourceSend requests, bend time, and follow each one through a fixed window, a sliding window and a token bucket. Every step shows the real numbers from the code.
Every request goes to all three algorithms at the same moment. Turn up the traffic, try a scenario, or click any dot to see why that request was allowed or rejected.
Each one schedules requests on the shared clock
One counter per clock-aligned window.
0 of 5 slots used in this window. They all empty at the boundary.
Send a request to watch this algorithm decide.
Weighs the previous window into a rolling estimate.
0×100%+0=0of 5
Send a request to watch this algorithm decide.
Tokens drip in steadily, and each request spends one.
+1 token every 1.2s · the bucket is full
Send a request to watch this algorithm decide.
Each algorithm keeps a few numbers per client, so memory per client never grows. They differ in how they turn those numbers into a yes or a no.
algorithm: 'fixed-window'Time is cut into clock-aligned windows. Each window has a counter that resets when the next window begins.
{ windowStart, count }algorithm: 'sliding-window'Keeps the current and previous window counts, then weighs the previous one by how much of it still overlaps a rolling window ending now.
{ windowStart, previous, current }algorithm: 'token-bucket'A bucket holds up to limit tokens and refills evenly over the window. Every request takes tokens out.
{ tokens, updatedAt }Every algorithm in the playground is one option away. The plugin sends standard RateLimit-* headers and aRetry-After on every 429.
import { Elysia } from 'elysia'
import { rateLimit } from 'elysia-rate-limiter'
const app = new Elysia()
.use(rateLimit({ limit: 100, window: '1m', algorithm: 'sliding-window' }))
.get('/', () => 'Hello')
.listen(3000)new Elysia()
.group('/api', (api) =>
api
.use(
rateLimit({
limit: 1_000,
window: '1m',
algorithm: 'token-bucket',
}),
)
.get('/search', search),
)The sliding window, which is the default. It behaves like a fixed window without the spike at the boundary, and still stores only three numbers per client. Pick the token bucket when short bursts are fine as long as the average stays steady, or the fixed window when a quota has to line up with the clock.
The counter resets at every boundary. A client that sends a full limit just before the reset and another full limit just after gets twice the limit in a moment. Run the “Window boundary trick” scenario in the playground and watch it happen.
No, it is an estimate. It assumes the previous window’s requests were spread evenly and counts only the share that still overlaps the rolling window. In practice it stays close to an exact log of timestamps while using constant memory.
RateLimit-Remaining is how many more requests fit right now. RateLimit-Reset and Retry-After are whole seconds, rounded up, until the limit resets or until a rejected client should try again. Each card shows the exact headers the plugin would send.
No. The real algorithm classes run in your browser on a simulated clock, so you can pause, speed up and replay traffic. The plugin runs the same evaluate() code against a memory or Redis store.