Go cookbook · Beginner
Route HTTP requests with the standard library ServeMux
You want method-based routes and path parameters without adding a router dependency.
Approach
Since Go 1.22, http.ServeMux patterns include the method and wildcards (GET /items/{id}); read them with
r.PathValue. Unmatched methods get 405 automatically. Configure server timeouts explicitly.
Solution
package main
import (
"encoding/json"
"log"
"net/http"
"strconv"
"time"
)
type Item struct {
ID int `json:"id"`
Name string `json:"name"`
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /items", listItems)
mux.HandleFunc("GET /items/{id}", getItem)
mux.HandleFunc("POST /items", createItem)
mux.HandleFunc("GET /files/{path...}", serveFile) // wildcard for the rest of the path
srv := &http.Server{
Addr: ":8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 15 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}
func getItem(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
http.Error(w, "id must be a number", http.StatusBadRequest)
return
}
writeJSON(w, http.StatusOK, Item{ID: id, Name: "Teapot"})
}
func listItems(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, []Item{{ID: 1, Name: "Mug"}})
}
func createItem(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusCreated, Item{ID: 2, Name: "Kettle"})
}
func serveFile(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("file: " + r.PathValue("path")))
}
func writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(v)
}
Pitfalls
- Using
http.ListenAndServewith the default server, which has no timeouts (slowloris). - Registering overlapping patterns that panic at startup; patterns must not be ambiguous.
- Writing headers after
WriteHeaderor the body; they're ignored.
Variations
- Group routes with prefixes by mounting sub-muxes:
mux.Handle("/api/", http.StripPrefix("/api", apiMux)). - Third-party routers (chi) add route groups and middleware stacks on the same
http.Handlerinterface.
Further reading: net/http ServeMux
Reference: https://pkg.go.dev/net/http#ServeMux
More Go recipes
- Benchmark code with testing.B.Loop
- Build a small, secure container image for a Go service
- Decode and validate JSON request bodies safely
- Embed static files and templates in the binary
- Load and validate configuration from environment variables
- Log structured JSON with slog and redact secrets
- Process jobs concurrently with a bounded worker pool
- Profile a running service with pprof