• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

.github/H02-Jun-2020-

_examples/H03-May-2022-

middleware/H02-Jun-2020-

testdata/H02-Jun-2020-

.gitignoreH A D02-Jun-202020

.travis.ymlH A D02-Jun-2020395

CHANGELOG.mdH A D02-Jun-20208.1 KiB

CONTRIBUTING.mdH A D02-Jun-20201 KiB

LICENSEH A D02-Jun-20201.1 KiB

README.mdH A D02-Jun-202018.6 KiB

chain.goH A D02-Jun-20201.5 KiB

chi.goH A D02-Jun-20204.5 KiB

context.goH A D02-Jun-20204.9 KiB

context_test.goH A D02-Jun-20201.9 KiB

mux.goH A D02-Jun-202014.6 KiB

mux_test.goH A D02-Jun-202048.6 KiB

tree.goH A D02-Jun-202019.5 KiB

tree_test.goH A D02-Jun-202022 KiB

README.md

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
442