RRuna

Rate Limit

Named rate-limit rules and HTTP middleware

rate provides named limiters. The default driver is memory and supports token bucket, fixed window, and sliding window. HTTP usage is provided by rate/middleware.

Typical use cases include limiting login attempts, SMS sends, API calls, and endpoints that are easy to abuse.

Install

go get github.com/duxweb/runa/rate

Install the Redis driver only when needed:

go get github.com/duxweb/runa/rate/redis

Connect to an application

package main

import (
    "context"
    "time"

    "github.com/duxweb/runa"
    "github.com/duxweb/runa/rate"
    ratemw "github.com/duxweb/runa/rate/middleware"
    "github.com/duxweb/runa/route"
)

func main() {
    app := runa.New()
    app.Install(
        route.Provider(route.Addr(":8080")),
        rate.Provider(rate.RegisterLimiter("api", rate.TokenBucket(60, time.Minute), rate.Key(rate.ByIP()))),
    )

    route.Default().Group("/api").Use(ratemw.Use("api")).Get("/ping", func(ctx *route.Context) error {
        return ctx.Text("pong")
    })

    if err := app.Run(context.Background()); err != nil {
        panic(err)
    }
}

Standalone New usage

registry := rate.New()
registry.Rate("api", rate.TokenBucket(60, time.Minute), rate.Key(rate.ByIP()))

result, err := registry.MustOf("api").Allow(context.Background(), "ip:127.0.0.1")
_ = result
_ = err

Config

rate reads rate.limiters.<name> and only applies config to limiters that have already been registered.

[rate.limiters.api]
driver = "memory"
algorithm = "token_bucket"
limit = 60
window = "1m"
burst = 10
key = ["ip", "route"]
Key Type Description
driver string driver name, default memory
algorithm string token_bucket, fixed_window, or sliding_window
limit int allowed count in the window
window duration counting window
burst int burst capacity
key []string key sources, such as ip, route, user, session
meta table custom metadata

Drivers

The built-in memory driver is suitable for single-process rate limiting. For multi-instance deployments, install rate/redis, register the Redis driver with RegisterDriver, then use rate.Use("redis") for the target limiter.

import (
    goredis "github.com/redis/go-redis/v9"
    rateredis "github.com/duxweb/runa/rate/redis"
)

client := goredis.NewClient(&goredis.Options{Addr: "127.0.0.1:6379"})

app.Install(rate.Provider(
    rate.RegisterDriver("redis", rateredis.Driver(client, rate.Prefix("runa:rate:"))),
    rate.RegisterLimiter("api", rate.Use("redis"), rate.FixedWindow(100, time.Minute), rate.Key(rate.ByIP())),
))

Which algorithm to choose

Algorithm Behavior Good for
TokenBucket Smooth limiting with some burst capacity General API rate limits
FixedWindow Simple counting in fixed windows Login or SMS attempts
SlidingWindow Smoother window boundaries and more precise counting Endpoints that need more accurate limits

If you are new to rate limiting, start with TokenBucket.

Key sources

rate.Key(rate.ByIP())
rate.Key(rate.ByRoute(), rate.ByUser())
rate.Key(rate.ByHeader("X-Tenant"))
rate.Key(rate.ByQuery("token"))
rate.Key(rate.ByFunc(func(ctx any) string { return "custom" }))

Common mistakes

Registering a limiter without using middleware

rate.Provider(...) only registers the rule. HTTP routes need ratemw.Use("api") for the rule to run.

Using the memory driver in multi-instance deployments

The memory driver only limits the current process. Use the Redis driver when multiple application instances serve the same traffic.

Using a key that is too broad

IP-only limits are simple but not always enough. Login, SMS, and API-key scenarios usually need more specific key sources.

API quick reference

  • rate.New() creates a standalone registry.
  • rate.Provider(...) connects to the framework lifecycle.
  • rate.Default() reads *rate.Registry from default DI.
  • rate.RegisterDriver(name, driver) registers a driver.
  • rate.RegisterLimiter(name, options...) registers a limiter.
  • rate/middleware.Use(name) creates HTTP rate-limit middleware.
Edit this page