Skip to content
/ Apex Public

Apex is a simple in-memory rate limiter in Go using the Token Bucket algorithm

Notifications You must be signed in to change notification settings

psidh/Apex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Apex : Rate Limiter

This project implements a simple in-memory rate limiter in Go using the Token Bucket algorithm. It is designed to sit in front of an HTTP handler as middleware and enforce per-client request limits.

The implementation is intentionally minimal and readable, focusing on correctness, concurrency safety, and clean HTTP semantics.


What Problem Does This Solve? (Most asked question to my every project)

In real systems, allowing unlimited requests from a single client can:

  • Overload your server
  • Cause unfair resource usage
  • Enable abuse (DDoS, brute force, scraping)

A rate limiter protects your service by controlling how many requests a client can make over time.


Core Concept: The Algorithm

Token Bucket Algorithm

Each client (identified by IP) gets its own token bucket.

How it works:

  • Each bucket has:
    • Capacity: maximum number of tokens
    • Refill rate: tokens added per second
  • Every request consumes tokens
  • If enough tokens exist → request allowed
  • If not → request rejected with 429 Too Many Requests

This allows:

  • Short bursts (up to capacity)
  • Sustained rate control (via refill rate)

High-Level Architecture


Client Request
↓
Rate Limiter Middleware (Apex)
↓
Token Bucket (per client IP)
↓
Allow or Reject
↓
HTTP Handler (Your Service)


Important Components

1. Limiter

Manages all client buckets.

type Limiter struct {
    buckets map[string]*TokenBucket
    mutex   sync.Mutex
}
  • One bucket per client key (IP)
  • Thread-safe access
  • Responsible for bucket creation and cleanup

2. TokenBucket

Represents rate limits for a single client.

type TokenBucket struct {
    capacity       int
    refillRate     int
    tokens         int
    lastRefillTime time.Time
    lastSeen       time.Time
    mutex          sync.Mutex
}

Responsibilities:

  • Track available tokens
  • Refill tokens based on elapsed time
  • Decide whether a request is allowed

3. Middleware

The rate limiter is applied as HTTP middleware.

limitedHandler := rateLimiterMiddleware(baseHandler)

It:

  • Extracts client IP

  • Checks allowance

  • Sets standard headers:

    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • Retry-After (on rejection)

4. Cleanup Goroutine

Idle clients are removed to avoid memory leaks.

limiter.StartCleanUp(10*time.Minute, 1*time.Minute)
  • Runs periodically
  • Deletes buckets not used recently

Client Identification

Clients are identified using:

  1. X-Forwarded-For (proxy-safe)
  2. X-Real-IP
  3. RemoteAddr fallback

This makes the limiter usable behind reverse proxies.


HTTP Behavior

Allowed Request

HTTP 200 OK
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 7

Rate Limited Request

HTTP 429 Too Many Requests
Retry-After: 1
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0

Advantages of the Design

  • In-memory and fast
  • Per-client isolation
  • Thread-safe with fine-grained locks
  • Deterministic token refill
  • Clean HTTP semantics

Limitations / Drawbacks

  • Single-node only (no distributed coordination)
  • Resets on process restart
  • IP-based (can be spoofed without proper proxy config)

When to Use This

  • Never (u have better options 😆)

How to Run

go run main.go

Server starts on:

http://localhost:8080

Final Note

This project is meant to be simple but correct. It intentionally avoids frameworks and abstractions so the core rate-limiting logic is easy to reason about.

If you understand this implementation deeply, you understand the foundation of most production rate limiters.

About

Apex is a simple in-memory rate limiter in Go using the Token Bucket algorithm

Resources

Stars

Watchers

Forks

Languages