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