Middleware Guide¶
GoRL ships with adapters for net/http, Gin, Fiber, and Echo.
Common Pattern¶
All middleware adapters do three things:
- extract a key from the request,
- call
Allow(ctx, key), - write rate-limit headers and either forward or deny the request.
Resource-scoped middleware adds one more selection step:
- extract a resource from the request,
- extract a key from the request,
- call
AllowResource(ctx, resource, key), - write rate-limit headers and either forward or deny the request.
sequenceDiagram
accTitle: HTTP middleware decision sequence
accDescr: Middleware evaluates a request and either calls the application handler, returns a 429 denial, or returns a 500 error.
participant Client
participant Middleware
participant Limiter
participant Handler
Client->>Middleware: HTTP request
Middleware->>Limiter: Allow or AllowResource
alt allowed
Limiter-->>Middleware: Allowed=true + metadata
Middleware->>Handler: Continue
Handler-->>Client: Application response + headers
else denied
Limiter-->>Middleware: Allowed=false + retry metadata
Middleware-->>Client: 429 + headers
else limiter error
Limiter-->>Middleware: error
Middleware-->>Client: 500 by default
end
net/http¶
package main
import (
"net/http"
"time"
"github.com/AliRizaAynaci/gorl/v2"
"github.com/AliRizaAynaci/gorl/v2/core"
mw "github.com/AliRizaAynaci/gorl/v2/middleware/http"
)
func main() {
limiter, _ := gorl.New(core.Config{
Strategy: core.SlidingWindow,
Limit: 10,
Window: time.Minute,
})
handler := mw.RateLimit(limiter, mw.Options{
KeyFunc: mw.KeyByIP(),
}, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
}))
http.ListenAndServe(":8080", handler)
}
Built-in Key Extractors¶
mw.KeyByIP()mw.KeyByHeader("X-API-Key")mw.KeyByPath()
Resource-Scoped net/http¶
resourceLimiter, _ := gorl.NewResourceLimiter(core.ResourceConfig{
Strategy: core.SlidingWindow,
DefaultPolicy: core.ResourcePolicy{
Limit: 100,
Window: time.Minute,
},
Resources: map[string]core.ResourcePolicy{
"/login": {Limit: 5, Window: time.Minute},
"/search": {Limit: 50, Window: time.Second},
},
})
handler := mw.RateLimitByResource(resourceLimiter, mw.Options{
KeyFunc: mw.KeyByIP(),
ResourceFunc: mw.ResourceByPath(),
}, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
}))
Important Note¶
middleware/http expects Options.KeyFunc to be provided by the caller.
Passing a zero-value Options causes a nil-function panic when a request is
handled. Treat the key extractor as required constructor input.
Gin¶
If KeyFunc is omitted, Gin defaults to c.ClientIP().
For resource-scoped limiting, RateLimitByResource defaults to c.FullPath()
when available and falls back to c.Request.URL.Path.
Fiber¶
If KeyFunc is omitted, Fiber defaults to c.IP().
For resource-scoped limiting, RateLimitByResource defaults to c.Path().
Echo¶
If KeyFunc is omitted, Echo defaults to c.RealIP().
For resource-scoped limiting, RateLimitByResource defaults to c.Path()
when available and falls back to c.Request().URL.Path.
Headers¶
Middleware adapters currently write these headers from core.Result:
RateLimit-LimitRateLimit-RemainingRateLimit-Resetwhen a positive reset duration is availableRetry-Afterwhen the request is denied and a positive retry delay is available
This keeps response headers aligned with reliable limiter metadata instead of forcing zero-value duration headers into every response.
Custom Error Handling¶
Each middleware package allows a custom denied or error handler so applications can standardize response bodies and logging.
Production checklist¶
- Derive keys only from authenticated or edge-normalized identity.
- Prefer matched route patterns over raw paths when path parameters would create high-cardinality resources.
- Keep denied handlers cheap; they execute when the service is already under pressure.
- Do not expose Redis errors or credentials in client responses.
- Decide whether rate limiting runs before or after authentication, tracing, decompression, and other expensive middleware.
- Verify header behavior through the actual reverse proxy or gateway because it may remove or rewrite response headers.