The route package provides an HTTP router with support for prefixed sub-routers, context injection, and instrumentation. It wraps the httprouter library with additional features for Prometheus components.
import "github.com/prometheus/common/route"This package offers a lightweight HTTP router that extends httprouter with context support for path parameters, URL prefix handling, and optional instrumentation hooks. It's designed for building HTTP APIs in Prometheus components.
type Router struct {
// HTTP router with context support
}HTTP router that wraps httprouter.Router with additional features including context parameter injection, prefix support, and instrumentation.
func (r *Router) WithInstrumentation(instrh func(handlerName string, handler http.HandlerFunc) http.HandlerFunc) *RouterReturns a new router that wraps all registered handlers with the provided instrumentation function. The instrumentation function receives the handler name and the handler function, allowing you to add metrics, logging, or other cross-cutting concerns.
Parameters:
instrh - Instrumentation function that wraps handlersReturns: New Router with instrumentation enabled
func (r *Router) WithPrefix(prefix string) *RouterReturns a new router that prefixes all registered routes with the given prefix. This is useful for mounting sub-routers under specific paths.
Parameters:
prefix - URL path prefix (e.g., "/api/v1")Returns: New Router with the specified prefix
func (r *Router) Get(path string, h http.HandlerFunc)Registers a new GET route.
Parameters:
path - URL path pattern (supports parameters like "/:id")h - Handler functionfunc (r *Router) Post(path string, h http.HandlerFunc)Registers a new POST route.
Parameters:
path - URL path patternh - Handler functionfunc (r *Router) Put(path string, h http.HandlerFunc)Registers a new PUT route.
Parameters:
path - URL path patternh - Handler functionfunc (r *Router) Del(path string, h http.HandlerFunc)Registers a new DELETE route.
Parameters:
path - URL path patternh - Handler functionfunc (r *Router) Options(path string, h http.HandlerFunc)Registers a new OPTIONS route.
Parameters:
path - URL path patternh - Handler functionfunc (r *Router) Head(path string, h http.HandlerFunc)Registers a new HEAD route.
Parameters:
path - URL path patternh - Handler functionfunc (r *Router) Redirect(w http.ResponseWriter, req *http.Request, path string, code int)Sends an internal HTTP redirect. Note that parameters from the request context are NOT automatically substituted into the path. If you need to include parameters in the redirect path, you must manually extract them using Param() and concatenate them into the path string.
Parameters:
w - Response writerreq - HTTP requestpath - Target path for the redirect (parameters must be manually concatenated)code - HTTP status code (typically 301, 302, 303, 307, or 308)func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)Implements http.Handler interface, allowing the Router to be used as an HTTP handler.
Parameters:
w - Response writerreq - HTTP requestfunc New() *RouterCreates and returns a new Router instance.
Returns: A new Router
func Param(ctx context.Context, p string) stringRetrieves a path parameter value from the request context. Returns an empty string if the parameter is not found.
Parameters:
ctx - Request contextp - Parameter nameReturns: Parameter value or empty string
Example:
func handler(w http.ResponseWriter, r *http.Request) {
id := route.Param(r.Context(), "id")
fmt.Fprintf(w, "ID: %s", id)
}func WithParam(ctx context.Context, p, v string) context.ContextReturns a new context with the specified parameter set to the given value. This is useful for testing or middleware that needs to inject parameters.
Parameters:
ctx - Parent contextp - Parameter namev - Parameter valueReturns: New context with the parameter set
func FileServe(dir string) http.HandlerFuncReturns a handler function that serves files from the specified directory. Routes using this handler must include a *filepath parameter in their path pattern.
Parameters:
dir - Directory path to serve files fromReturns: Handler function that serves files
Example:
router.Get("/static/*filepath", route.FileServe("./public"))package main
import (
"fmt"
"net/http"
"github.com/prometheus/common/route"
)
func main() {
router := route.New()
// Register routes
router.Get("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, World!")
})
router.Get("/users/:id", func(w http.ResponseWriter, r *http.Request) {
id := route.Param(r.Context(), "id")
fmt.Fprintf(w, "User ID: %s\n", id)
})
router.Post("/users", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Create user")
})
// Start server
http.ListenAndServe(":8080", router)
}package main
import (
"fmt"
"net/http"
"github.com/prometheus/common/route"
)
func main() {
router := route.New()
// Create API v1 sub-router
v1 := router.WithPrefix("/api/v1")
v1.Get("/users", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "List users (v1)")
})
v1.Get("/users/:id", func(w http.ResponseWriter, r *http.Request) {
id := route.Param(r.Context(), "id")
fmt.Fprintf(w, "Get user %s (v1)\n", id)
})
// Create API v2 sub-router
v2 := router.WithPrefix("/api/v2")
v2.Get("/users", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "List users (v2)")
})
http.ListenAndServe(":8080", router)
}package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/prometheus/common/route"
)
func instrumentHandler(name string, handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
handler(w, r)
duration := time.Since(start)
log.Printf("%s %s took %v", name, r.URL.Path, duration)
}
}
func main() {
router := route.New().WithInstrumentation(instrumentHandler)
router.Get("/users/:id", func(w http.ResponseWriter, r *http.Request) {
id := route.Param(r.Context(), "id")
fmt.Fprintf(w, "User: %s\n", id)
})
http.ListenAndServe(":8080", router)
}package main
import (
"fmt"
"net/http"
"github.com/prometheus/common/route"
)
func main() {
router := route.New()
// API routes
router.Get("/api/status", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "OK")
})
// Serve static files from ./public directory
router.Get("/static/*filepath", route.FileServe("./public"))
// Serve index.html for root
router.Get("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./public/index.html")
})
http.ListenAndServe(":8080", router)
}package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/prometheus/common/route"
)
type User struct {
ID string `json:"id"`
Name string `json:"name"`
}
func main() {
router := route.New()
api := router.WithPrefix("/api/v1")
// List users
api.Get("/users", func(w http.ResponseWriter, r *http.Request) {
users := []User{
{ID: "1", Name: "Alice"},
{ID: "2", Name: "Bob"},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
})
// Get user by ID
api.Get("/users/:id", func(w http.ResponseWriter, r *http.Request) {
id := route.Param(r.Context(), "id")
user := User{ID: id, Name: fmt.Sprintf("User %s", id)}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
})
// Create user
api.Post("/users", func(w http.ResponseWriter, r *http.Request) {
var user User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
})
// Update user
api.Put("/users/:id", func(w http.ResponseWriter, r *http.Request) {
id := route.Param(r.Context(), "id")
var user User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
user.ID = id
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
})
// Delete user
api.Del("/users/:id", func(w http.ResponseWriter, r *http.Request) {
id := route.Param(r.Context(), "id")
w.WriteHeader(http.StatusNoContent)
fmt.Fprintf(w, "Deleted user %s", id)
})
http.ListenAndServe(":8080", router)
}package main
import (
"net/http"
"github.com/prometheus/common/route"
)
func main() {
router := route.New()
// Redirect old path to new path
router.Get("/old-path", func(w http.ResponseWriter, r *http.Request) {
router.Redirect(w, r, "/new-path", http.StatusMovedPermanently)
})
router.Get("/new-path", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("New path"))
})
// Redirect with parameter
router.Get("/old/users/:id", func(w http.ResponseWriter, r *http.Request) {
id := route.Param(r.Context(), "id")
router.Redirect(w, r, "/api/v1/users/"+id, http.StatusMovedPermanently)
})
http.ListenAndServe(":8080", router)
}The router uses httprouter's path syntax:
Named parameters: /:name - matches a single path segment
/users/:id matches /users/123route.Param(ctx, "id")Catch-all parameters: /*name - matches everything after the prefix
/files/*filepath matches /files/docs/readme.mdroute.Param(ctx, "filepath")All path parameters are automatically injected into the request context. Always use route.Param() to retrieve them in your handlers.
The WithPrefix() and WithInstrumentation() methods return new Router instances, so you can chain them:
router := route.New().
WithInstrumentation(instrument).
WithPrefix("/api")All handler functions must match the standard http.HandlerFunc signature:
func(w http.ResponseWriter, r *http.Request)