Skip to content

devfullcycle/ratelimiter

Folders and files

NameName
Last commit message
Last commit date
Feb 8, 2025
Feb 8, 2025
Feb 8, 2025
Feb 8, 2025
Feb 8, 2025
Feb 8, 2025
Feb 8, 2025
Feb 8, 2025
Feb 8, 2025
Feb 8, 2025
Feb 8, 2025

Repository files navigation

Rate Limiter

A flexible and modular rate limiting solution for REST APIs in Go.

Overview

This rate limiter provides a robust solution for controlling API request rates in Go applications. It features:

  • IP-based rate limiting with configurable limits
  • Atomic operations for thread-safe request counting
  • Temporary blocking of clients exceeding limits
  • Structured error responses with retry information
  • Modular design for easy extension
  • Memory storage with future support for distributed storage

Installation

go get github.com/devfullcycle/ratelimiter

Quick Start

package main

import (
    "github.com/devfullcycle/ratelimiter/middleware"
    "github.com/devfullcycle/ratelimiter/ratelimiter"
    "github.com/devfullcycle/ratelimiter/storage"
    "log/slog"
    "net/http"
    "os"
)

func main() {
    // Initialize logger
    logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

    // Create memory storage
    store := storage.NewMemoryStorage()

    // Create rate limiter with default options (100 req/min)
    limiter := ratelimiter.New(store)

    // Create middleware
    rateLimitMiddleware := middleware.NewRateLimitMiddleware(limiter, logger)

    // Apply to your handlers
    http.Handle("/", rateLimitMiddleware.Handler(
        http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            w.Write([]byte("Hello, World!"))
        }),
    ))

    http.ListenAndServe(":8080", nil)
}

Configuration

The rate limiter supports the following configuration options:

Default Values

  • Maximum requests: 100 per minute
  • Block duration: 1 minute
  • Storage: Memory-based
  • Client identification: IP-based

Customization Options

limiter := ratelimiter.New(
    store,
    ratelimiter.WithMaxRequests(200),           // Change request limit
    ratelimiter.WithTimeWindow(time.Hour),      // Change time window
    ratelimiter.WithBlockDuration(time.Minute), // Change block duration
)

Rate Limit Response

When a client exceeds the rate limit:

HTTP Response

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60

JSON Response

{
    "error": "Rate limit exceeded",
    "limit": 100,
    "requests_made": 120,
    "retry_after": "2024-02-08T14:30:00.000Z"
}

Storage Backends

Memory Storage (Default)

The default memory storage is suitable for single-instance deployments. See the Quick Start section for usage.

Redis Storage

For distributed environments, Redis storage backend is available. To use Redis:

  1. Configure Redis environment variables:
REDIS_HOST=localhost    # Redis server hostname
REDIS_PORT=6379        # Redis server port
REDIS_PASSWORD=        # Optional Redis password
REDIS_DB=0            # Redis database number
  1. Example code:
package main

import (
    "github.com/devfullcycle/ratelimiter/middleware"
    "github.com/devfullcycle/ratelimiter/ratelimiter"
    "github.com/devfullcycle/ratelimiter/storage"
)

func main() {
    // Initialize Redis storage
    cfg := storage.DefaultRedisConfig()
    client := storage.NewRedisClient(cfg)
    store := storage.NewRedisStorage(client)

    // Create rate limiter with Redis storage
    limiter := ratelimiter.New(store)
    
    // ... rest of your application setup
}
  1. Running with Docker Compose:
# Start Redis and the application
docker compose up -d

# Run the Redis example
docker compose exec app sh -c "cd /app && go run examples/redis/redis.go"

Development and Testing

Prerequisites

  • Docker and Docker Compose
  • Go 1.23 or higher (if running locally)

Running the Example

  1. Start the development container:

    docker compose up -d
  2. Run the example server:

    docker compose exec app sh -c "cd /app && go run examples/memory/memory.go"
  3. Test rate limiting with hey:

    # Test normal requests (should succeed)
    docker compose exec app hey -n 80 -c 10 http://localhost:8080/
    
    # Test rate limit exceeded
    docker compose exec app hey -n 150 -c 50 http://localhost:8080/

Running Tests

docker compose exec app sh -c "cd /app && go test ./..."

Architecture

The rate limiter follows a modular design with three main components:

  1. Core Rate Limiter (ratelimiter)

    • Handles rate limiting logic
    • Configurable via options pattern
    • Thread-safe operations
  2. Storage Interface (storage)

    • Pluggable storage backends
    • Default memory implementation
    • Atomic operations for thread safety
  3. HTTP Middleware (middleware)

    • Easy integration with HTTP servers
    • Structured logging with slog
    • Standard error responses

License

MIT License