...

Text file src/github.com/go-chi/chi/README.md

Documentation: github.com/go-chi/chi

     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