Request Context
Store request-scoped values, access route parameters, and share data safely between middleware and handlers using router.Context.
The router package provides a lightweight request-scoped context through *router.Context.
func(w http.ResponseWriter, req *http.Request, ctx *router.Context)The context is designed for:
- route parameters
- middleware communication
- request-scoped storage
- authentication data
- database connections
- tracing metadata
- reusable request state
The context exists only for the lifetime of the request.
Accessing Route Parameters
Route parameters can be accessed using ctx.Param.
Example route:
r.GET("/users/{id}", handler)Handler:
func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
id := ctx.Param("id")
w.Write([]byte(id))
}Request:
/users/42Captured value:
42Storing Request Values
Middleware and handlers can store values inside the request context.
ctx.Set("user_id", 42)Later in the request lifecycle:
userID := ctx.Get("user_id")This allows middleware and handlers to share request-scoped state safely.
Example
package main
import (
"net/http"
"github.com/netlifeguru/router"
)
func main() {
r := router.New()
r.Use(func(next router.HandlerFunc) router.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
ctx.Set("request_source", "middleware")
next(w, req, ctx)
}
})
r.GET("/users/{id}", func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
id := ctx.Param("id")
source := ctx.Get("request_source")
w.Write([]byte(
"user_id=" + id + ", source=" + source.(string),
))
})
r.ListenAndServe(":8000")
}Request:
/users/42Response:
user_id=42, source=middlewareMiddleware Communication
One of the primary uses of router.Context is communication between middleware and handlers.
Example authentication middleware:
func Auth(next router.HandlerFunc) router.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
ctx.Set("user_id", 123)
next(w, req, ctx)
}
}Handler:
r.GET("/profile", func(w http.ResponseWriter, req *http.Request, ctx *router.Context) {
userID := ctx.Get("user_id")
w.Write([]byte(fmt.Sprintf("user=%v", userID)))
})This avoids repeatedly parsing authentication state in every handler.
Request-Scoped Storage
The request context is isolated per request.
Values stored inside one request are never shared with other requests.
This makes it safe for concurrent workloads and HTTP servers handling many requests simultaneously.
Example use cases:
| Use Case | Example |
|---|---|
| Authentication | user_id, roles |
| Database Connection | request transaction |
| Request Tracing | request ID |
| Localization | language settings |
| Feature Flags | request-specific flags |
| Metrics | timing metadata |
Context Lifecycle
The context exists only while the request is being processed.
After the request completes, the context is automatically released and reused internally by the router.
Applications should never store references to *router.Context outside the request lifecycle.
Incorrect:
globalCtx = ctxCorrect:
value := ctx.Get("key")and copy only the required values.
Performance
The request context is internally pooled to reduce allocations during high request throughput.
This helps minimize:
- heap allocations
- GC pressure
- per-request overhead
The pooling mechanism is fully automatic and transparent to applications.
Context vs context.Context
router.Context is separate from Go’s standard:
context.ContextThe router context is optimized for:
- route parameters
- request-scoped mutable values
- middleware communication
For cancellation, deadlines, or propagation across services, continue using:
req.Context()Both can be used together.
Example:
ctx.Set("user_id", 42)
requestCtx := req.Context()Notes
router.Context is designed for lightweight request-scoped data sharing inside the HTTP pipeline.
It should not replace application services, dependency injection, or persistent storage.