limiter

package module
v2.0.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 21, 2025 License: MIT Imports: 14 Imported by: 0

README

Limiter

Go Report Card

A high-performance rate limiting middleware supporting multiple Go web frameworks (Fiber, Gin, Echo, Chi, and standard library) with Redis and in-memory storage, implementing multiple rate-limiting algorithms.

Table of Contents

Features

  • 🚀 Multiple Algorithms: Token Bucket, Sliding Window, and Fixed Window
  • 🖥️ Multi-Framework Support: Fiber, Gin, Echo, Chi, and standard library
  • 💾 Storage Options: Redis (for distributed systems) and in-memory (for single-instance)
  • High Performance: Minimal overhead with efficient algorithms
  • 🔧 Customizable: Flexible key generation and response handling
  • 📊 RFC Compliance: Standard RateLimit headers (RFC 6585)

Installation

go get github.com/NarmadaWeb/limiter/v2

Usage

The limiter supports multiple web frameworks. Choose the appropriate middleware method for your framework:

  • Fiber: l.FiberMiddleware(config)
  • Gin: l.GinMiddleware(config)
  • Echo: l.EchoMiddleware(config)
  • StdLib: l.StdLibMiddleware(config) (works with Chi, Gorilla Mux, etc.)
Basic Example (Fiber)
package main

import (
    "time"

    "github.com/gofiber/fiber/v2"
    "github.com/NarmadaWeb/limiter/v2"
)

func main() {
    app := fiber.New()

    // Initialize with default in-memory store
    limiterCfg := limiter.Config{
        MaxRequests: 100,
        Window:      1 * time.Minute,
        Algorithm:   "sliding-window",
    }

    l, err := limiter.New(limiterCfg)
    if err != nil {
        panic(err)
    }

    app.Use(l.FiberMiddleware(limiter.FiberConfig{}))

    app.Get("/", func(c *fiber.Ctx) error {
        return c.SendString("Hello, World!")
    })

    app.Listen(":3000")
}
With Redis (Fiber)
package main

import (
    "time"

    "github.com/NarmadaWeb/limiter/v2"
    "github.com/gofiber/fiber/v2"
    "github.com/redis/go-redis/v9"
)

func main() {
    app := fiber.New()

    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    limiterCfg := limiter.Config{
        RedisClient: rdb,
        MaxRequests: 200,
        Window:      5 * time.Minute,
        Algorithm:   "token-bucket",
    }

    l, err := limiter.New(limiterCfg)
    if err != nil {
        panic(err)
    }
    app.Use(l.FiberMiddleware(limiter.FiberConfig{}))

    app.Get("/", func(c *fiber.Ctx) error {
        return c.SendString("Hello with Redis!")
    })

    app.Listen(":3000")
}
Gin Framework
package main

import (
    "time"

    "github.com/NarmadaWeb/limiter/v2"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    limiterCfg := limiter.Config{
        MaxRequests: 100,
        Window:      1 * time.Minute,
        Algorithm:   "sliding-window",
    }

    l, err := limiter.New(limiterCfg)
    if err != nil {
        panic(err)
    }

    r.Use(l.GinMiddleware(limiter.GinConfig{}))

    r.GET("/", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "Hello from Gin!"})
    })

    r.Run(":8080")
}
Echo Framework
package main

import (
    "net/http"
    "time"

    "github.com/NarmadaWeb/limiter/v2"
    "github.com/labstack/echo/v4"
)

func main() {
    e := echo.New()

    limiterCfg := limiter.Config{
        MaxRequests: 100,
        Window:      1 * time.Minute,
        Algorithm:   "sliding-window",
    }

    l, err := limiter.New(limiterCfg)
    if err != nil {
        panic(err)
    }

    e.Use(l.EchoMiddleware(limiter.EchoConfig{}))

    e.GET("/", func(c echo.Context) error {
        return c.JSON(http.StatusOK, map[string]string{"message": "Hello from Echo!"})
    })

    e.Logger.Fatal(e.Start(":8080"))
}
Standard Library / Chi Router
package main

import (
    "encoding/json"
    "net/http"
    "time"

    "github.com/NarmadaWeb/limiter/v2"
    "github.com/go-chi/chi/v5"
)

func main() {
    r := chi.NewRouter()

    limiterCfg := limiter.Config{
        MaxRequests: 100,
        Window:      1 * time.Minute,
        Algorithm:   "sliding-window",
    }

    l, err := limiter.New(limiterCfg)
    if err != nil {
        panic(err)
    }

    r.Use(l.StdLibMiddleware(limiter.StdLibConfig{}))

    r.Get("/", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]string{"message": "Hello from Chi!"})
    })

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

Configuration Options

Core Configuration
Option Type Description
RedisClient *redis.Client Redis client instance (optional)
RedisURL string Redis connection URL (alternative to RedisClient)
MaxRequests int Maximum allowed requests per window
Window time.Duration Duration of the rate limit window (e.g., 1*time.Minute)
Algorithm string Rate limiting algorithm (token-bucket, sliding-window, fixed-window)
Framework-Specific Configuration

Each framework has its own configuration struct with framework-specific handlers:

FiberConfig
Option Type Description
KeyGenerator func(*fiber.Ctx) string Custom function to generate rate limit keys (default: client IP)
SkipSuccessful bool Don't count successful requests (status < 400)
LimitReachedHandler fiber.Handler Custom handler when limit is reached
ErrorHandler func(*fiber.Ctx, error) error Custom error handler for storage/configuration errors
GinConfig
Option Type Description
KeyGenerator func(*gin.Context) string Custom function to generate rate limit keys (default: client IP)
SkipSuccessful bool Don't count successful requests (status < 400)
LimitReachedHandler func(*gin.Context) Custom handler when limit is reached
ErrorHandler func(*gin.Context, error) Custom error handler for storage/configuration errors
EchoConfig
Option Type Description
KeyGenerator func(echo.Context) string Custom function to generate rate limit keys (default: real IP)
SkipSuccessful bool Don't count successful requests (status < 400)

| LimitReachedHandler | func(echo.Context) error | Custom handler when limit is reached | | ErrorHandler | func(echo.Context, error) error | Custom error handler for storage/configuration errors |

StdLibConfig
Option Type Description
KeyGenerator func(*http.Request) string Custom function to generate rate limit keys (default: client IP)
SkipSuccessful bool Don't count successful requests (status < 400)
LimitReachedHandler http.HandlerFunc Custom handler when limit is reached
ErrorHandler func(http.ResponseWriter, *http.Request, error) Custom error handler for storage/configuration errors

Response Headers

The middleware adds these standard headers to responses:

  • X-RateLimit-Limit: Maximum requests allowed
  • X-RateLimit-Remaining: Remaining requests in current window
  • X-RateLimit-Reset: Unix timestamp when limit resets
  • RateLimit-Policy: Formal policy description

Algorithms

  1. Token Bucket

    • Smooth bursting allowed
    • Gradually refills tokens at steady rate
    • Good for evenly distributed loads
  2. Sliding Window

    • Precise request counting
    • Tracks exact request timestamps
    • Prevents bursts at window edges
  3. Fixed Window

    • Simple implementation

    • Counts requests per fixed interval

    • May allow bursts at window boundaries

Examples

See the examples directory for complete implementations for all supported frameworks:

Fiber Examples
  • Basic - Simple rate limiting with in-memory storage

  • Redis - Distributed rate limiting using Redis

  • Multiple Limiters - Using different rate limiters for different routes

  • Error Handling - Custom error and rate limit exceeded handlers

  • Custom Key - Custom key generation for rate limiting buckets

Gin Examples
  • Gin Basic - Rate limiting with Gin framework
Echo Examples
Standard Library Examples
  • StdLib/Chi - Rate limiting with standard library and Chi router

Contributing

We welcome contributions! Please see our CONTRIBUTING.md for guidelines.

License

MIT © NarmadaWeb - See LICENSE for details.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidAlgorithm = errors.New("invalid rate limiting algorithm")
	ErrStorage          = errors.New("storage error")
	ErrRedisConnection  = errors.New("redis connection error")
	ErrInvalidConfig    = errors.New("invalid configuration")
)

Functions

This section is empty.

Types

type Config

type Config struct {
	// Redis Configuration for starting limiter
	RedisClient *redis.Client
	RedisURL    string

	// Rate Limiter configuration
	MaxRequests int
	Window      time.Duration

	// value "token-bucket", "sliding-window" and "fixed-window"
	Algorithm string
}

Config holds the core configuration for the rate limiter. Framework-specific settings are handled in their respective middleware generators.

type EchoConfig added in v2.0.6

type EchoConfig struct {
	KeyGenerator        func(c echo.Context) string
	LimitReachedHandler func(c echo.Context) error
	ErrorHandler        func(c echo.Context, err error) error
	Skipsuccessfull     bool
}

type FiberConfig added in v2.0.6

type FiberConfig struct {
	KeyGenerator        func(c *fiber.Ctx) string
	LimitReachedHandler fiber.Handler
	ErrorHandler        func(c *fiber.Ctx, err error) error
	Skipsuccessfull     bool
}

type GinConfig added in v2.0.6

type GinConfig struct {
	KeyGenerator        func(c *gin.Context) string
	LimitReachedHandler func(c *gin.Context)
	ErrorHandler        func(c *gin.Context, err error)
	Skipsuccessfull     bool
}

type Limiter

type Limiter struct {
	// contains filtered or unexported fields
}

func New

func New(config Config) (*Limiter, error)

func (*Limiter) Close

func (l *Limiter) Close() error

func (*Limiter) EchoMiddleware added in v2.0.6

func (l *Limiter) EchoMiddleware(cfg EchoConfig) echo.MiddlewareFunc

func (*Limiter) FiberMiddleware added in v2.0.6

func (l *Limiter) FiberMiddleware(cfg FiberConfig) fiber.Handler

func (*Limiter) GinMiddleware added in v2.0.6

func (l *Limiter) GinMiddleware(cfg GinConfig) gin.HandlerFunc

func (*Limiter) StdLibMiddleware added in v2.0.6

func (l *Limiter) StdLibMiddleware(cfg StdLibConfig) func(http.Handler) http.Handler

StdLibMiddleware creates a standard net/http middleware. This works for Chi, Go Standard Library, and any framework compatible with http.Handler.

type MemoryEntries

type MemoryEntries struct {
	// contains filtered or unexported fields
}

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

func NewMemoryStore

func NewMemoryStore() *MemoryStore

func (*MemoryStore) Close

func (m *MemoryStore) Close() error

func (*MemoryStore) Get

func (m *MemoryStore) Get(ctx context.Context, key string) (int, error)

func (*MemoryStore) Rollback

func (m *MemoryStore) Rollback(ctx context.Context, key string) error

func (*MemoryStore) Set

func (m *MemoryStore) Set(ctx context.Context, key string, value int, expiration time.Duration) error

func (*MemoryStore) Take

func (m *MemoryStore) Take(ctx context.Context, key string, maxRequests int, window time.Duration, algorithm string) (bool, int, time.Time, error)

type RedisStore

type RedisStore struct {
	// contains filtered or unexported fields
}

func NewRedisStore

func NewRedisStore(client *redis.Client) *RedisStore

func (*RedisStore) Close

func (r *RedisStore) Close() error

func (*RedisStore) Get

func (r *RedisStore) Get(ctx context.Context, key string) (int, error)

func (*RedisStore) Rollback

func (r *RedisStore) Rollback(ctx context.Context, key string) error

func (*RedisStore) Set

func (r *RedisStore) Set(ctx context.Context, key string, value int, expiration time.Duration) error

func (*RedisStore) Take

func (r *RedisStore) Take(ctx context.Context, key string, maxRequests int, window time.Duration, algorithm string) (bool, int, time.Time, error)

type StdLibConfig added in v2.0.6

type StdLibConfig struct {
	KeyGenerator        func(r *http.Request) string
	LimitReachedHandler http.HandlerFunc
	ErrorHandler        func(w http.ResponseWriter, r *http.Request, err error)
	Skipsuccessfull     bool
}

type Store

type Store interface {
	Take(ctx context.Context, key string, maxRequests int, window time.Duration, algorithm string) (bool, int, time.Time, error)
	Rollback(ctx context.Context, key string) error
	Get(ctx context.Context, key string) (int, error)
	Set(ctx context.Context, key string, value int, expiration time.Duration) error
}

Store defines the interface for limiter Store have 4 values Take, Rollback, Get and Set

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL