Learn Chi - Routing, Params & Patterns
Series/Learn Chi/Episode 4
Episode 4 of 23

Learn Chi - Routing, Params & Patterns

This episode dissects chi's routing system: method routing, path params with curly braces, wildcards for the rest of the path, and regex patterns for param validation. You will also learn to handle NotFound and MethodNotAllowed, and the matching order that determines which route gets selected.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

This is the most central episode of the whole series: routing, params, and patterns. This is where you learn how a URL is translated into a handler — from declaring r.Get and r.Post, capturing dynamic values in the middle of the path, to handling routes that aren't found.

After this episode, you'll never write flat, repetitive routes again. Every URL can capture data, validate formats, and handle fallbacks cleanly.

Method Routing

The Four Main Methods

chi provides a helper for every HTTP method:

Routing by method
r.Get("/users", listUsers)
r.Post("/users", createUser)
r.Put("/users/{id}", updateUser)
r.Delete("/users/{id}", deleteUser)

r.Post("/users", createUser) only serves the POST method; a GET request to the same path falls into the MethodNotAllowed handler (covered below). This is clean REST without branching on method inside the handler.

Handle for Other Methods

For methods without a helper — like PATCH or OPTIONS — use r.Method:

Methods without helpers
r.Method(http.MethodPatch, "/users/{id}", patchUser)
r.Method(http.MethodOptions, "/users", corsPreflight)

r.Method(http.MethodPatch, "/users/{id}", patchUser) takes the method name as the first string and the handler as the last argument.

Path Params

Capturing Dynamic Values

Dynamic params are written with curly braces inside the pattern:

Path param
r.Get("/users/{id}", func(w http.ResponseWriter, req *http.Request) {
    id := chi.URLParam(req, "id")
    w.Write([]byte("user " + id))
})

chi.URLParam(req, "id") reads the value captured by the {id} pattern. When a request arrives at /users/42, the variable id holds "42".

Params Are Required in v5

Important to remember: in chi v5, params are required. The route /users/{id} will not match /users — for that, you have to register a separate /users route. This is a deliberate simplification compared to version 4, which had an optional param syntax.

Regex and Wildcard

Regex Patterns

Constrain the param format with a regex inside the curly braces:

Regex on a param
r.Get("/users/{id:[0-9]+}", numericUser)
r.Get("/articles/{slug:[a-z0-9-]+}", articleBySlug)

The {id:[0-9]+} pattern only matches digits. A request to /users/abc won't reach this handler — it falls to NotFound, or to another route that matches better.

Wildcard for the Rest of the Path

A wildcard captures the whole remaining path, useful for files and dynamic prefixes:

Wildcard for the rest of the path
r.Get("/files/{path:*}", func(w http.ResponseWriter, req *http.Request) {
    path := chi.URLParam(req, "path")
    w.Write([]byte("file: " + path))
})

{path:*} matches /files/a/b/c.txt with the value a/b/c.txt. chi.URLParam(req, "path") returns that whole string, including the slashes inside it.

Complete Route Patterns

Pattern summary
r.Get("/users/{id}", handlerUser)
r.Get("/users/{id:[0-9]+}", handlerUserNumeric)
r.Get("/files/{path:*}", handlerFile)
r.Post("/users", handlerCreateUser)

Notice how chi chooses: a more specific regex beats an ordinary param. handlerUserNumeric will catch /users/123, while handlerUser catches /users/abc.

NotFound and MethodNotAllowed

Fallback Handlers

When no pattern matches, chi calls the NotFound handler. When the path matches but the method doesn't, chi calls MethodNotAllowed:

Fallback routes
r.NotFound(func(w http.ResponseWriter, req *http.Request) {
    http.Error(w, "halaman tidak ditemukan", http.StatusNotFound)
})
 
r.MethodNotAllowed(func(w http.ResponseWriter, req *http.Request) {
    http.Error(w, "method tidak diizinkan", http.StatusMethodNotAllowed)
})

r.NotFound(handler) and r.MethodNotAllowed(handler) replace the default messages with your own JSON response or custom page.

Testing the Fallback

Test params and fallbacks
curl -i http://localhost:8080/users/42
curl -i http://localhost:8080/tidak-ada
curl -i -X DELETE http://localhost:8080/users/42

curl -i -X DELETE http://localhost:8080/users/42 tests a method that isn't registered — you should see status 405 Method Not Allowed with your custom message.

Route Matching Order

Rules to Remember

Understanding the matching order saves you from mysterious bugs:

  • Specificity wins: regex and literals are more specific than ordinary params and wildcards.
  • List in the order you need: when two patterns are equally specific, the one registered first wins.
  • Wildcards go last: {path:*} is always the last resort for a segment.

So the patterns r.Get("/users/new", ...) and r.Get("/users/{id}", ...) can coexist: a request to /users/new is routed to the literal handler, while /users/7 goes to the param handler.

Conclusion

Key takeaways:

  • Method helpers: r.Get, r.Post, r.Put, r.Delete; other methods via r.Method.
  • Path param {id} is read with chi.URLParam(req, "id").
  • v5 params are required; parent and child routes must be registered separately.
  • Regex {id:[0-9]+} constrains the format; wildcard {path:*} captures the rest of the path.
  • r.NotFound and r.MethodNotAllowed handle fallbacks.
  • Matching is based on specificity, then registration order.

In the next episode 5 we'll organize routes at a larger scale: subrouters, groups, and mounting — separating resources with r.Route, sharing middleware with r.Group, and attaching sub-apps with r.Mount.

Learn Chi - Routing, Params & Patterns | Learn Chi