In Go's http.ServeMux, what does the pattern "GET /items/{id}" match, and how does the handler read id?
answer
- the method is part of the pattern text
- braces mark exactly one path segment
- GET also answers HEAD
- the wildcard is looked up by its name
- the value arrives as an unvalidated string
basics
~10 sIt matches GET and HEAD requests whose path is exactly two segments: the literal items followed by any single segment. The handler reads that segment with r.PathValue("id"), which returns it as a string.
solid answer
~40 sSince Go 1.22 a `ServeMux` pattern has the shape `[METHOD ][HOST]/[PATH]`, so `"GET /items/{id}"` constrains both the method and the path. The method part matches GET, and a GET pattern also matches HEAD; a pattern with no method matches every method. `{id}` is a wildcard that matches exactly **one** path segment, so `/items/42` matches but `/items/42/reviews` does not and `/items` does not. A wildcard must be a whole segment — `"/items/item-{id}"` is invalid and `mux.HandleFunc` panics on it at registration. Inside the handler, `r.PathValue("id")` returns the matched segment as a string, already unescaped, and returns `""` if no such wildcard matched. You still parse and validate it yourself before using it as a database key. A POST to the same path gets a 405 with an `Allow` header, not a 404.
code
go · 10 linesmux := http.NewServeMux()
mux.HandleFunc("GET /items/{id}", func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id") // always a string, already unescaped
fmt.Fprintf(w, "item %s", id)
})
// GET /items/42 -> matches, id == "42"
// HEAD /items/42 -> matches the same pattern
// GET /items/42/reviews -> no match: {id} covers one segment only
// POST /items/42 -> 405, with an Allow headergo deeper
Be ready to write the registration line and read the value back: HandleFunc with a method and a {name} segment, then r.PathValue in the handler. Say out loud that the value arrives as a string you still have to validate.
Explain the segment rule precisely: a {name} wildcard covers exactly one segment, a trailing {name...} covers the rest, and an invalid wildcard panics at registration rather than failing at request time.
Show that you check the unhappy paths: what a wrong method returns, what an empty PathValue means, and how the value is parsed and bounded before it ever reaches a query or a file path.
Frame what changes for a team when the standard library matches methods and path variables: a routing dependency stops being automatic, and the argument for one has to be made on features it actually adds.
## What a ServeMux is `http.ServeMux` is the standard library's HTTP request multiplexer. You register patterns against handlers, and the mux — which is itself an `http.Handler` — compares each incoming request against every registered pattern and dispatches to the winner. `http.NewServeMux()` gives you a fresh one. ## The pattern grammar Since Go 1.22 a pattern looks like: ``` [METHOD ][HOST]/[PATH] ``` All three parts are optional, and `"/"` is a valid pattern. If a method is present it must be followed by whitespace before the path. So `"GET /items/{id}"` is a method (`GET`), no host, and a path with one literal segment and one wildcard segment. **Method rules.** A pattern with no method matches every method. A pattern with `GET` matches GET *and* HEAD, because the standard library treats HEAD as a GET whose body is discarded. Any other method must match exactly. **Host rules.** A pattern with no host matches every host; `"api.example.com/items/{id}"` matches only requests whose Host header names that host, with the port stripped. **Wildcard rules.** `{id}` matches exactly one path segment, ending at the next literal slash. The name must be a valid Go identifier. Wildcards must be complete segments: they have to be preceded by a slash and followed by a slash or the end of the pattern, so `"/items/item-{id}"` is not a valid pattern and registering it panics. The trailing form `{rest...}` matches all remaining segments including the slashes between them, and is only legal at the very end of a pattern. ## Reading the value `r.PathValue(name)` returns the string the named wildcard matched, or the empty string if the request did not match a pattern with that wildcard. Two things surprise newcomers: 1. **It is always a string.** There is no typed binding, no integer constraint on the wildcard, and no validation. If the id is a database key you convert it yourself (`strconv.Atoi`, a UUID parse, a lookup against an allowlist) and return 400 on failure. A route that reads `r.PathValue("id")` and interpolates it straight into a query has done nothing for you that a query parameter would not. 2. **The value is already unescaped.** Pattern paths and request paths are matched segment by segment after unescaping, so a request for `/items/a%2Fb` is one segment whose value is `a/b`, and `r.PathValue("id")` gives you `a/b` rather than the percent-encoded form. There is a matching `r.SetPathValue(name, value)`, which exists mainly so tests and wrappers can synthesise a request as though it had matched a pattern. ## The three outcomes for /items/42 Given only `"GET /items/{id}"` registered: - `GET /items/42` — matches; `PathValue("id")` is `"42"`. - `HEAD /items/42` — matches the same pattern. - `POST /items/42` — the *path* matches a registered pattern but the *method* does not, so the mux answers 405 Method Not Allowed and sets an `Allow` header listing the methods registered for that path. This is a real improvement over hand-rolled routing, where a wrong method usually fell through to 404 and told the client nothing. - `GET /items` or `GET /items/42/reviews` — no registered pattern matches, so 404. ## Why this matters for the shape of a handler Before Go 1.22 the standard mux matched only path prefixes, so nearly every real service either split `r.URL.Path` by hand or pulled in a routing dependency. Method-and-wildcard patterns mean an ordinary CRUD resource — list, fetch by id, update by id — can be registered directly against the standard library, with the method check done by the mux instead of a `switch r.Method` at the top of every handler. That is the single biggest practical change to `net/http` in years, and it is why an interviewer asks the question at all. If you must run on a toolchain older than 1.22, or you need the old prefix semantics back for one deployment, `GODEBUG=httpmuxgo121=1` restores the pre-1.22 matcher.
- Does that pattern also serve a HEAD request for the same path?Yes. A pattern whose method is GET matches both GET and HEAD requests, so you do not register HEAD separately. The server discards the body of a HEAD response for you. Any other method — POST, PUT, DELETE — must be spelled out in its own pattern.
- Only "GET /items/{id}" is registered and a POST arrives at /items/42. What does the client get?405 Method Not Allowed, with an `Allow` header naming the methods registered for that path — not a 404. The mux distinguishes "this path is unknown" from "this path exists but not for your method", which is the response clients and API tests expect.
- Is "/items/item-{id}" a legal ServeMux pattern?No. A wildcard has to be a complete path segment: preceded by a slash and followed by a slash or the end of the pattern. `"/items/item-{id}"` mixes a literal prefix with a wildcard inside one segment, so `Handle` and `HandleFunc` panic at registration. Match the whole segment and strip the prefix in the handler.
saying these in an interview costs you the question
- Says a {id} wildcard spans several path segments
- Claims the standard mux still cannot match methods or path variables
- Expects a wrong method to produce 404 rather than 405
- Assumes PathValue returns a validated or typed value
- Writes a wildcard inside a segment, like /items/item-{id}