1# <img alt="chi" src="https://cdn.rawgit.com/go-chi/chi/master/_examples/chi.svg" width="220" /> 2 3 4[![GoDoc Widget]][GoDoc] [![Travis Widget]][Travis] 5 6`chi` is a lightweight, idiomatic and composable router for building Go HTTP services. It's 7especially good at helping you write large REST API services that are kept maintainable as your 8project grows and changes. `chi` is built on the new `context` package introduced in Go 1.7 to 9handle signaling, cancelation and request-scoped values across a handler chain. 10 11The focus of the project has been to seek out an elegant and comfortable design for writing 12REST API servers, written during the development of the Pressly API service that powers our 13public API service, which in turn powers all of our client-side applications. 14 15The key considerations of chi's design are: project structure, maintainability, standard http 16handlers (stdlib-only), developer productivity, and deconstructing a large system into many small 17parts. The core router `github.com/go-chi/chi` is quite small (less than 1000 LOC), but we've also 18included some useful/optional subpackages: [middleware](/middleware), [render](https://github.com/go-chi/render) 19and [docgen](https://github.com/go-chi/docgen). We hope you enjoy it too! 20 21## Install 22 23`go get -u github.com/go-chi/chi/v5` 24 25 26## Features 27 28* **Lightweight** - cloc'd in ~1000 LOC for the chi router 29* **Fast** - yes, see [benchmarks](#benchmarks) 30* **100% compatible with net/http** - use any http or middleware pkg in the ecosystem that is also compatible with `net/http` 31* **Designed for modular/composable APIs** - middlewares, inline middlewares, route groups and sub-router mounting 32* **Context control** - built on new `context` package, providing value chaining, cancellations and timeouts 33* **Robust** - in production at Pressly, CloudFlare, Heroku, 99Designs, and many others (see [discussion](https://github.com/go-chi/chi/issues/91)) 34* **Doc generation** - `docgen` auto-generates routing documentation from your source to JSON or Markdown 35* **Go.mod support** - as of v5, go.mod support (see [CHANGELOG](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)) 36* **No external dependencies** - plain ol' Go stdlib + net/http 37 38 39## Examples 40 41See [_examples/](https://github.com/go-chi/chi/blob/master/_examples/) for a variety of examples. 42 43 44**As easy as:** 45 46```go 47package main 48 49import ( 50 "net/http" 51 52 "github.com/go-chi/chi/v5" 53 "github.com/go-chi/chi/v5/middleware" 54) 55 56func main() { 57 r := chi.NewRouter() 58 r.Use(middleware.Logger) 59 r.Get("/", func(w http.ResponseWriter, r *http.Request) { 60 w.Write([]byte("welcome")) 61 }) 62 http.ListenAndServe(":3000", r) 63} 64``` 65 66**REST Preview:** 67 68Here is a little preview of how routing looks like with chi. Also take a look at the generated routing docs 69in JSON ([routes.json](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.json)) and in 70Markdown ([routes.md](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.md)). 71 72I highly recommend reading the source of the [examples](https://github.com/go-chi/chi/blob/master/_examples/) listed 73above, they will show you all the features of chi and serve as a good form of documentation. 74 75```go 76import ( 77 //... 78 "context" 79 "github.com/go-chi/chi/v5" 80 "github.com/go-chi/chi/v5/middleware" 81) 82 83func main() { 84 r := chi.NewRouter() 85 86 // A good base middleware stack 87 r.Use(middleware.RequestID) 88 r.Use(middleware.RealIP) 89 r.Use(middleware.Logger) 90 r.Use(middleware.Recoverer) 91 92 // Set a timeout value on the request context (ctx), that will signal 93 // through ctx.Done() that the request has timed out and further 94 // processing should be stopped. 95 r.Use(middleware.Timeout(60 * time.Second)) 96 97 r.Get("/", func(w http.ResponseWriter, r *http.Request) { 98 w.Write([]byte("hi")) 99 }) 100 101 // RESTy routes for "articles" resource 102 r.Route("/articles", func(r chi.Router) { 103 r.With(paginate).Get("/", listArticles) // GET /articles 104 r.With(paginate).Get("/{month}-{day}-{year}", listArticlesByDate) // GET /articles/01-16-2017 105 106 r.Post("/", createArticle) // POST /articles 107 r.Get("/search", searchArticles) // GET /articles/search 108 109 // Regexp url parameters: 110 r.Get("/{articleSlug:[a-z-]+}", getArticleBySlug) // GET /articles/home-is-toronto 111 112 // Subrouters: 113 r.Route("/{articleID}", func(r chi.Router) { 114 r.Use(ArticleCtx) 115 r.Get("/", getArticle) // GET /articles/123 116 r.Put("/", updateArticle) // PUT /articles/123 117 r.Delete("/", deleteArticle) // DELETE /articles/123 118 }) 119 }) 120 121 // Mount the admin sub-router 122 r.Mount("/admin", adminRouter()) 123 124 http.ListenAndServe(":3333", r) 125} 126 127func ArticleCtx(next http.Handler) http.Handler { 128 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 129 articleID := chi.URLParam(r, "articleID") 130 article, err := dbGetArticle(articleID) 131 if err != nil { 132 http.Error(w, http.StatusText(404), 404) 133 return 134 } 135 ctx := context.WithValue(r.Context(), "article", article) 136 next.ServeHTTP(w, r.WithContext(ctx)) 137 }) 138} 139 140func getArticle(w http.ResponseWriter, r *http.Request) { 141 ctx := r.Context() 142 article, ok := ctx.Value("article").(*Article) 143 if !ok { 144 http.Error(w, http.StatusText(422), 422) 145 return 146 } 147 w.Write([]byte(fmt.Sprintf("title:%s", article.Title))) 148} 149 150// A completely separate router for administrator routes 151func adminRouter() http.Handler { 152 r := chi.NewRouter() 153 r.Use(AdminOnly) 154 r.Get("/", adminIndex) 155 r.Get("/accounts", adminListAccounts) 156 return r 157} 158 159func AdminOnly(next http.Handler) http.Handler { 160 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 161 ctx := r.Context() 162 perm, ok := ctx.Value("acl.permission").(YourPermissionType) 163 if !ok || !perm.IsAdmin() { 164 http.Error(w, http.StatusText(403), 403) 165 return 166 } 167 next.ServeHTTP(w, r) 168 }) 169} 170``` 171 172 173## Router interface 174 175chi's router is based on a kind of [Patricia Radix trie](https://en.wikipedia.org/wiki/Radix_tree). 176The router is fully compatible with `net/http`. 177 178Built on top of the tree is the `Router` interface: 179 180```go 181// Router consisting of the core routing methods used by chi's Mux, 182// using only the standard net/http. 183type Router interface { 184 http.Handler 185 Routes 186 187 // Use appends one or more middlewares onto the Router stack. 188 Use(middlewares ...func(http.Handler) http.Handler) 189 190 // With adds inline middlewares for an endpoint handler. 191 With(middlewares ...func(http.Handler) http.Handler) Router 192 193 // Group adds a new inline-Router along the current routing 194 // path, with a fresh middleware stack for the inline-Router. 195 Group(fn func(r Router)) Router 196 197 // Route mounts a sub-Router along a `pattern`` string. 198 Route(pattern string, fn func(r Router)) Router 199 200 // Mount attaches another http.Handler along ./pattern/* 201 Mount(pattern string, h http.Handler) 202 203 // Handle and HandleFunc adds routes for `pattern` that matches 204 // all HTTP methods. 205 Handle(pattern string, h http.Handler) 206 HandleFunc(pattern string, h http.HandlerFunc) 207 208 // Method and MethodFunc adds routes for `pattern` that matches 209 // the `method` HTTP method. 210 Method(method, pattern string, h http.Handler) 211 MethodFunc(method, pattern string, h http.HandlerFunc) 212 213 // HTTP-method routing along `pattern` 214 Connect(pattern string, h http.HandlerFunc) 215 Delete(pattern string, h http.HandlerFunc) 216 Get(pattern string, h http.HandlerFunc) 217 Head(pattern string, h http.HandlerFunc) 218 Options(pattern string, h http.HandlerFunc) 219 Patch(pattern string, h http.HandlerFunc) 220 Post(pattern string, h http.HandlerFunc) 221 Put(pattern string, h http.HandlerFunc) 222 Trace(pattern string, h http.HandlerFunc) 223 224 // NotFound defines a handler to respond whenever a route could 225 // not be found. 226 NotFound(h http.HandlerFunc) 227 228 // MethodNotAllowed defines a handler to respond whenever a method is 229 // not allowed. 230 MethodNotAllowed(h http.HandlerFunc) 231} 232 233// Routes interface adds two methods for router traversal, which is also 234// used by the github.com/go-chi/docgen package to generate documentation for Routers. 235type Routes interface { 236 // Routes returns the routing tree in an easily traversable structure. 237 Routes() []Route 238 239 // Middlewares returns the list of middlewares in use by the router. 240 Middlewares() Middlewares 241 242 // Match searches the routing tree for a handler that matches 243 // the method/path - similar to routing a http request, but without 244 // executing the handler thereafter. 245 Match(rctx *Context, method, path string) bool 246} 247``` 248 249Each routing method accepts a URL `pattern` and chain of `handlers`. The URL pattern 250supports named params (ie. `/users/{userID}`) and wildcards (ie. `/admin/*`). URL parameters 251can be fetched at runtime by calling `chi.URLParam(r, "userID")` for named parameters 252and `chi.URLParam(r, "*")` for a wildcard parameter. 253 254 255### Middleware handlers 256 257chi's middlewares are just stdlib net/http middleware handlers. There is nothing special 258about them, which means the router and all the tooling is designed to be compatible and 259friendly with any middleware in the community. This offers much better extensibility and reuse 260of packages and is at the heart of chi's purpose. 261 262Here is an example of a standard net/http middleware where we assign a context key `"user"` 263the value of `"123"`. This middleware sets a hypothetical user identifier on the request 264context and calls the next handler in the chain. 265 266```go 267// HTTP middleware setting a value on the request context 268func MyMiddleware(next http.Handler) http.Handler { 269 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 270 // create new context from `r` request context, and assign key `"user"` 271 // to value of `"123"` 272 ctx := context.WithValue(r.Context(), "user", "123") 273 274 // call the next handler in the chain, passing the response writer and 275 // the updated request object with the new context value. 276 // 277 // note: context.Context values are nested, so any previously set 278 // values will be accessible as well, and the new `"user"` key 279 // will be accessible from this point forward. 280 next.ServeHTTP(w, r.WithContext(ctx)) 281 }) 282} 283``` 284 285 286### Request handlers 287 288chi uses standard net/http request handlers. This little snippet is an example of a http.Handler 289func that reads a user identifier from the request context - hypothetically, identifying 290the user sending an authenticated request, validated+set by a previous middleware handler. 291 292```go 293// HTTP handler accessing data from the request context. 294func MyRequestHandler(w http.ResponseWriter, r *http.Request) { 295 // here we read from the request context and fetch out `"user"` key set in 296 // the MyMiddleware example above. 297 user := r.Context().Value("user").(string) 298 299 // respond to the client 300 w.Write([]byte(fmt.Sprintf("hi %s", user))) 301} 302``` 303 304 305### URL parameters 306 307chi's router parses and stores URL parameters right onto the request context. Here is 308an example of how to access URL params in your net/http handlers. And of course, middlewares 309are able to access the same information. 310 311```go 312// HTTP handler accessing the url routing parameters. 313func MyRequestHandler(w http.ResponseWriter, r *http.Request) { 314 // fetch the url parameter `"userID"` from the request of a matching 315 // routing pattern. An example routing pattern could be: /users/{userID} 316 userID := chi.URLParam(r, "userID") 317 318 // fetch `"key"` from the request context 319 ctx := r.Context() 320 key := ctx.Value("key").(string) 321 322 // respond to the client 323 w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key))) 324} 325``` 326 327 328## Middlewares 329 330chi comes equipped with an optional `middleware` package, providing a suite of standard 331`net/http` middlewares. Please note, any middleware in the ecosystem that is also compatible 332with `net/http` can be used with chi's mux. 333 334### Core middlewares 335 336---------------------------------------------------------------------------------------------------- 337| chi/middleware Handler | description | 338| :--------------------- | :---------------------------------------------------------------------- | 339| [AllowContentEncoding] | Enforces a whitelist of request Content-Encoding headers | 340| [AllowContentType] | Explicit whitelist of accepted request Content-Types | 341| [BasicAuth] | Basic HTTP authentication | 342| [Compress] | Gzip compression for clients that accept compressed responses | 343| [ContentCharset] | Ensure charset for Content-Type request headers | 344| [CleanPath] | Clean double slashes from request path | 345| [GetHead] | Automatically route undefined HEAD requests to GET handlers | 346| [Heartbeat] | Monitoring endpoint to check the servers pulse | 347| [Logger] | Logs the start and end of each request with the elapsed processing time | 348| [NoCache] | Sets response headers to prevent clients from caching | 349| [Profiler] | Easily attach net/http/pprof to your routers | 350| [RealIP] | Sets a http.Request's RemoteAddr to either X-Real-IP or X-Forwarded-For | 351| [Recoverer] | Gracefully absorb panics and prints the stack trace | 352| [RequestID] | Injects a request ID into the context of each request | 353| [RedirectSlashes] | Redirect slashes on routing paths | 354| [RouteHeaders] | Route handling for request headers | 355| [SetHeader] | Short-hand middleware to set a response header key/value | 356| [StripSlashes] | Strip slashes on routing paths | 357| [Throttle] | Puts a ceiling on the number of concurrent requests | 358| [Timeout] | Signals to the request context when the timeout deadline is reached | 359| [URLFormat] | Parse extension from url and put it on request context | 360| [WithValue] | Short-hand middleware to set a key/value on the request context | 361---------------------------------------------------------------------------------------------------- 362 363[AllowContentEncoding]: https://pkg.go.dev/github.com/go-chi/chi/middleware#AllowContentEncoding 364[AllowContentType]: https://pkg.go.dev/github.com/go-chi/chi/middleware#AllowContentType 365[BasicAuth]: https://pkg.go.dev/github.com/go-chi/chi/middleware#BasicAuth 366[Compress]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Compress 367[ContentCharset]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ContentCharset 368[CleanPath]: https://pkg.go.dev/github.com/go-chi/chi/middleware#CleanPath 369[GetHead]: https://pkg.go.dev/github.com/go-chi/chi/middleware#GetHead 370[GetReqID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#GetReqID 371[Heartbeat]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Heartbeat 372[Logger]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Logger 373[NoCache]: https://pkg.go.dev/github.com/go-chi/chi/middleware#NoCache 374[Profiler]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Profiler 375[RealIP]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RealIP 376[Recoverer]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Recoverer 377[RedirectSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RedirectSlashes 378[RequestLogger]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RequestLogger 379[RequestID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RequestID 380[RouteHeaders]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RouteHeaders 381[SetHeader]: https://pkg.go.dev/github.com/go-chi/chi/middleware#SetHeader 382[StripSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#StripSlashes 383[Throttle]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Throttle 384[ThrottleBacklog]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleBacklog 385[ThrottleWithOpts]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleWithOpts 386[Timeout]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Timeout 387[URLFormat]: https://pkg.go.dev/github.com/go-chi/chi/middleware#URLFormat 388[WithLogEntry]: https://pkg.go.dev/github.com/go-chi/chi/middleware#WithLogEntry 389[WithValue]: https://pkg.go.dev/github.com/go-chi/chi/middleware#WithValue 390[Compressor]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Compressor 391[DefaultLogFormatter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#DefaultLogFormatter 392[EncoderFunc]: https://pkg.go.dev/github.com/go-chi/chi/middleware#EncoderFunc 393[HeaderRoute]: https://pkg.go.dev/github.com/go-chi/chi/middleware#HeaderRoute 394[HeaderRouter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#HeaderRouter 395[LogEntry]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LogEntry 396[LogFormatter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LogFormatter 397[LoggerInterface]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LoggerInterface 398[ThrottleOpts]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleOpts 399[WrapResponseWriter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#WrapResponseWriter 400 401### Extra middlewares & packages 402 403Please see https://github.com/go-chi for additional packages. 404 405-------------------------------------------------------------------------------------------------------------------- 406| package | description | 407|:---------------------------------------------------|:------------------------------------------------------------- 408| [cors](https://github.com/go-chi/cors) | Cross-origin resource sharing (CORS) | 409| [docgen](https://github.com/go-chi/docgen) | Print chi.Router routes at runtime | 410| [jwtauth](https://github.com/go-chi/jwtauth) | JWT authentication | 411| [hostrouter](https://github.com/go-chi/hostrouter) | Domain/host based request routing | 412| [httplog](https://github.com/go-chi/httplog) | Small but powerful structured HTTP request logging | 413| [httprate](https://github.com/go-chi/httprate) | HTTP request rate limiter | 414| [httptracer](https://github.com/go-chi/httptracer) | HTTP request performance tracing library | 415| [httpvcr](https://github.com/go-chi/httpvcr) | Write deterministic tests for external sources | 416| [stampede](https://github.com/go-chi/stampede) | HTTP request coalescer | 417-------------------------------------------------------------------------------------------------------------------- 418 419 420## context? 421 422`context` is a tiny pkg that provides simple interface to signal context across call stacks 423and goroutines. It was originally written by [Sameer Ajmani](https://github.com/Sajmani) 424and is available in stdlib since go1.7. 425 426Learn more at https://blog.golang.org/context 427 428and.. 429* Docs: https://golang.org/pkg/context 430* Source: https://github.com/golang/go/tree/master/src/context 431 432 433## Benchmarks 434 435The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark 436 437Results as of Nov 29, 2020 with Go 1.15.5 on Linux AMD 3950x 438 439```shell 440BenchmarkChi_Param 3075895 384 ns/op 400 B/op 2 allocs/op 441BenchmarkChi_Param5 2116603 566 ns/op 400 B/op 2 allocs/op 442BenchmarkChi_Param20 964117 1227 ns/op 400 B/op 2 allocs/op 443BenchmarkChi_ParamWrite 2863413 420 ns/op 400 B/op 2 allocs/op 444BenchmarkChi_GithubStatic 3045488 395 ns/op 400 B/op 2 allocs/op 445BenchmarkChi_GithubParam 2204115 540 ns/op 400 B/op 2 allocs/op 446BenchmarkChi_GithubAll 10000 113811 ns/op 81203 B/op 406 allocs/op 447BenchmarkChi_GPlusStatic 3337485 359 ns/op 400 B/op 2 allocs/op 448BenchmarkChi_GPlusParam 2825853 423 ns/op 400 B/op 2 allocs/op 449BenchmarkChi_GPlus2Params 2471697 483 ns/op 400 B/op 2 allocs/op 450BenchmarkChi_GPlusAll 194220 5950 ns/op 5200 B/op 26 allocs/op 451BenchmarkChi_ParseStatic 3365324 356 ns/op 400 B/op 2 allocs/op 452BenchmarkChi_ParseParam 2976614 404 ns/op 400 B/op 2 allocs/op 453BenchmarkChi_Parse2Params 2638084 439 ns/op 400 B/op 2 allocs/op 454BenchmarkChi_ParseAll 109567 11295 ns/op 10400 B/op 52 allocs/op 455BenchmarkChi_StaticAll 16846 71308 ns/op 62802 B/op 314 allocs/op 456``` 457 458Comparison with other routers: https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc 459 460NOTE: the allocs in the benchmark above are from the calls to http.Request's 461`WithContext(context.Context)` method that clones the http.Request, sets the `Context()` 462on the duplicated (alloc'd) request and returns it the new request object. This is just 463how setting context on a request in Go works. 464 465 466## Credits 467 468* Carl Jackson for https://github.com/zenazn/goji 469 * Parts of chi's thinking comes from goji, and chi's middleware package 470 sources from goji. 471* Armon Dadgar for https://github.com/armon/go-radix 472* Contributions: [@VojtechVitek](https://github.com/VojtechVitek) 473 474We'll be more than happy to see [your contributions](./CONTRIBUTING.md)! 475 476 477## Beyond REST 478 479chi is just a http router that lets you decompose request handling into many smaller layers. 480Many companies use chi to write REST services for their public APIs. But, REST is just a convention 481for managing state via HTTP, and there's a lot of other pieces required to write a complete client-server 482system or network of microservices. 483 484Looking beyond REST, I also recommend some newer works in the field: 485* [webrpc](https://github.com/webrpc/webrpc) - Web-focused RPC client+server framework with code-gen 486* [gRPC](https://github.com/grpc/grpc-go) - Google's RPC framework via protobufs 487* [graphql](https://github.com/99designs/gqlgen) - Declarative query language 488* [NATS](https://nats.io) - lightweight pub-sub 489 490 491## License 492 493Copyright (c) 2015-present [Peter Kieltyka](https://github.com/pkieltyka) 494 495Licensed under [MIT License](./LICENSE) 496 497[GoDoc]: https://pkg.go.dev/github.com/go-chi/chi?tab=versions 498[GoDoc Widget]: https://godoc.org/github.com/go-chi/chi?status.svg 499[Travis]: https://travis-ci.org/go-chi/chi 500[Travis Widget]: https://travis-ci.org/go-chi/chi.svg?branch=master 501