or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

assets.mdconfig.mdexpfmt.mdhelpers-templates.mdindex.mdmodel.mdpromslog-flag.mdpromslog.mdroute.mdserver.mdversion.md
tile.json

route.mddocs/

Route Package

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

import "github.com/prometheus/common/route"

Overview

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.

Core Types

Router

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.

Methods

WithInstrumentation
func (r *Router) WithInstrumentation(instrh func(handlerName string, handler http.HandlerFunc) http.HandlerFunc) *Router

Returns 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 handlers

Returns: New Router with instrumentation enabled

WithPrefix
func (r *Router) WithPrefix(prefix string) *Router

Returns 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

Get
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 function
Post
func (r *Router) Post(path string, h http.HandlerFunc)

Registers a new POST route.

Parameters:

  • path - URL path pattern
  • h - Handler function
Put
func (r *Router) Put(path string, h http.HandlerFunc)

Registers a new PUT route.

Parameters:

  • path - URL path pattern
  • h - Handler function
Del
func (r *Router) Del(path string, h http.HandlerFunc)

Registers a new DELETE route.

Parameters:

  • path - URL path pattern
  • h - Handler function
Options
func (r *Router) Options(path string, h http.HandlerFunc)

Registers a new OPTIONS route.

Parameters:

  • path - URL path pattern
  • h - Handler function
Head
func (r *Router) Head(path string, h http.HandlerFunc)

Registers a new HEAD route.

Parameters:

  • path - URL path pattern
  • h - Handler function
Redirect
func (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 writer
  • req - HTTP request
  • path - Target path for the redirect (parameters must be manually concatenated)
  • code - HTTP status code (typically 301, 302, 303, 307, or 308)
ServeHTTP
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 writer
  • req - HTTP request

Functions

New

func New() *Router

Creates and returns a new Router instance.

Returns: A new Router

Param

func Param(ctx context.Context, p string) string

Retrieves a path parameter value from the request context. Returns an empty string if the parameter is not found.

Parameters:

  • ctx - Request context
  • p - Parameter name

Returns: 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)
}

WithParam

func WithParam(ctx context.Context, p, v string) context.Context

Returns 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 context
  • p - Parameter name
  • v - Parameter value

Returns: New context with the parameter set

FileServe

func FileServe(dir string) http.HandlerFunc

Returns 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 from

Returns: Handler function that serves files

Example:

router.Get("/static/*filepath", route.FileServe("./public"))

Usage Examples

Basic Router

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)
}

Prefixed Sub-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)
}

With Instrumentation

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)
}

File Serving

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)
}

RESTful API

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)
}

URL Redirects

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)
}

Path Parameter Syntax

The router uses httprouter's path syntax:

  • Named parameters: /:name - matches a single path segment

    • Example: /users/:id matches /users/123
    • Retrieved with: route.Param(ctx, "id")
  • Catch-all parameters: /*name - matches everything after the prefix

    • Example: /files/*filepath matches /files/docs/readme.md
    • Retrieved with: route.Param(ctx, "filepath")

Notes

Context Parameters

All path parameters are automatically injected into the request context. Always use route.Param() to retrieve them in your handlers.

Method Chaining

The WithPrefix() and WithInstrumentation() methods return new Router instances, so you can chain them:

router := route.New().
    WithInstrumentation(instrument).
    WithPrefix("/api")

Handler Signatures

All handler functions must match the standard http.HandlerFunc signature:

func(w http.ResponseWriter, r *http.Request)