1. Project Overview & Quickstart (go-chi/chi)
File: README.md
chi is a lightweight, idiomatic and composable router for building Go HTTP services. It's
especially good at helping you write large REST API services that are kept maintainable as your
project grows and changes. chi is built on the new context package introduced in Go 1.7 to
handle signaling, cancelation and request-scoped values across a handler chain.
The focus of the project has been to seek out an elegant and comfortable design for writing
REST API servers, written during the development of the Pressly API service that powers our
public API service, which in turn powers all of our client-side applications.
The key considerations of chi's design are: project structure, maintainability, standard http
handlers (stdlib-only), developer productivity, and deconstructing a large system into many small
parts. The core router github.com/go-chi/chi is quite small (less than 1000 LOC), but we've also
included some useful/optional subpackages: middleware, render
and docgen. We hope you enjoy it too!
Install
go get -u github.com/go-chi/chi/v5Features
- Lightweight - cloc'd in ~1000 LOC for the chi router
- Fast - yes, see benchmarks
- 100% compatible with net/http - use any http or middleware pkg in the ecosystem that is also compatible with
net/http - Designed for modular/composable APIs - middlewares, inline middlewares, route groups and sub-router mounting
- Context control - built on new
contextpackage, providing value chaining, cancellations and timeouts - Robust - in production at Pressly, Cloudflare, Heroku, 99Designs, and many others (see discussion)
- Doc generation -
docgenauto-generates routing documentation from your source to JSON or Markdown - Go.mod support - as of v5, go.mod support (see CHANGELOG)
- No external dependencies - plain ol' Go stdlib + net/http
Examples
See _examples/ for a variety of examples.
As easy as:
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome"))
})
http.ListenAndServe(":3000", r)
}REST Preview:
Here is a little preview of what routing looks like with chi. Also take a look at the generated routing docs
in JSON (routes.json) and in
Markdown (routes.md).
I highly recommend reading the source of the examples listed
above, they will show you all the features of chi and serve as a good form of documentation.
/* Detailed source-code truncated for AI context efficiency. */Router interface
chi's router is based on a kind of Patricia Radix trie.
The router is fully compatible with net/http.
Built on top of the tree is the Router interface:
/* Detailed source-code truncated for AI context efficiency. */Each routing method accepts a URL pattern and chain of handlers. The URL pattern
supports named params (ie. /users/{userID}) and wildcards (ie. /admin/*). URL parameters
can be fetched at runtime by calling chi.URLParam(r, "userID") for named parameters
and chi.URLParam(r, "*") for a wildcard parameter.
Middleware handlers
chi's middlewares are just stdlib net/http middleware handlers. There is nothing special
about them, which means the router and all the tooling is designed to be compatible and
friendly with any middleware in the community. This offers much better extensibility and reuse
of packages and is at the heart of chi's purpose.
Here is an example of a standard net/http middleware where we assign a context key "user"
the value of "123". This middleware sets a hypothetical user identifier on the request
context and calls the next handler in the chain.
// HTTP middleware setting a value on the request context
func MyMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// create new context from `r` request context, and assign key `"user"`
// to value of `"123"`
ctx := context.WithValue(r.Context(), "user", "123")
// call the next handler in the chain, passing the response writer and
// the updated request object with the new context value.
//
// note: context.Context values are nested, so any previously set
// values will be accessible as well, and the new `"user"` key
// will be accessible from this point forward.
next.ServeHTTP(w, r.WithContext(ctx))
})
}Request handlers
chi uses standard net/http request handlers. This little snippet is an example of a http.Handler
func that reads a user identifier from the request context - hypothetically, identifying
the user sending an authenticated request, validated+set by a previous middleware handler.
// HTTP handler accessing data from the request context.
func MyRequestHandler(w http.ResponseWriter, r *http.Request) {
// here we read from the request context and fetch out `"user"` key set in
// the MyMiddleware example above.
user := r.Context().Value("user").(string)
// respond to the client
w.Write([]byte(fmt.Sprintf("hi %s", user)))
}URL parameters
chi's router parses and stores URL parameters right onto the request context. Here is
an example of how to access URL params in your net/http handlers. And of course, middlewares
are able to access the same information.
// HTTP handler accessing the url routing parameters.
func MyRequestHandler(w http.ResponseWriter, r *http.Request) {
// fetch the url parameter `"userID"` from the request of a matching
// routing pattern. An example routing pattern could be: /users/{userID}
userID := chi.URLParam(r, "userID")
// fetch `"key"` from the request context
ctx := r.Context()
key := ctx.Value("key").(string)
// respond to the client
w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key)))
}Middlewares
chi comes equipped with an optional middleware package, providing a suite of standardnet/http middlewares. Please note, any middleware in the ecosystem that is also compatible
with net/http can be used with chi's mux.
Core middlewares
| chi/middleware Handler | description |
|---|---|
| AllowContentEncoding | Enforces a whitelist of request Content-Encoding headers |
| AllowContentType | Explicit whitelist of accepted request Content-Types |
| BasicAuth | Basic HTTP authentication |
| Compress | Gzip compression for clients that accept compressed responses |
| ContentCharset | Ensure charset for Content-Type request headers |
| CleanPath | Clean double slashes from request path |
| GetHead | Automatically route undefined HEAD requests to GET handlers |
| Heartbeat | Monitoring endpoint to check the servers pulse |
| Logger | Logs the start and end of each request with the elapsed processing time |
| NoCache | Sets response headers to prevent clients from caching |
| Profiler | Easily attach net/http/pprof to your routers |
| ClientIPFromHeader | Capture client IP from a trusted single-IP header (X-Real-IP, CF-Connecting-IP, ...) |
| ClientIPFromXFF | Capture client IP from X-Forwarded-For, skipping listed trusted CIDR prefixes |
| ClientIPFromXFFTrustedProxies | Capture client IP from X-Forwarded-For given a fixed number of trusted proxies |
| ClientIPFromRemoteAddr | Capture client IP from the TCP RemoteAddr (server directly on the public internet) |
| RealIP | Deprecated โ vulnerable to IP spoofing; use ClientIPFromXFF or another ClientIPFrom* middleware |
| Recoverer | Gracefully absorb panics and prints the stack trace |
| RequestID | Injects a request ID into the context of each request |
| RedirectSlashes | Redirect slashes on routing paths |
| RouteHeaders | Route handling for request headers |
| SetHeader | Short-hand middleware to set a response header key/value |
| StripSlashes | Strip slashes on routing paths |
| Sunset | Sunset set Deprecation/Sunset header to response |
| Throttle | Puts a ceiling on the number of concurrent requests |
| Timeout | Signals to the request context when the timeout deadline is reached |
| URLFormat | Parse extension from url and put it on request context |
| WithValue | Short-hand middleware to set a key/value on the request context |
Choosing a ClientIP middleware
The legacy RealIP middleware is deprecated โ it is vulnerable to IP spoofing
(GHSA-3fxj-6jh8-hvhx, GHSA-rjr7-jggh-pgcp, GHSA-9g5q-2w5x-hmxf) and mutatesr.RemoteAddr. Use one of the four ClientIPFrom* middlewares instead โ pick
exactly one based on your network setup โ and read the resulting IP with
GetClientIP (string) or GetClientIPAddr (netip.Addr):
| Your setup | Use |
|---|---|
| Directly on the public internet, no proxy | middleware.ClientIPFromRemoteAddr |
Behind nginx (X-Real-IP), Cloudflare (CF-Connecting-IP), Apache (X-Client-IP) |
middleware.ClientIPFromHeader("<your-trusted-header>") |
| Behind one or more proxies whose IP ranges you can list | middleware.ClientIPFromXFF("10.0.0.0/8", ...) |
| Behind a known, fixed number of proxies with dynamic IPs | middleware.ClientIPFromXFFTrustedProxies(2) |
r := chi.NewRouter()
r.Use(middleware.RequestID)
// Pick exactly one. Examples for common deployments:
// Direct internet exposure (no proxy):
// r.Use(middleware.ClientIPFromRemoteAddr)
// Behind Cloudflare:
// r.Use(middleware.ClientIPFromHeader("CF-Connecting-IP"))
// Behind AWS CloudFront (or any proxy fleet with known CIDRs):
r.Use(middleware.ClientIPFromXFF(
"13.32.0.0/15", // CloudFront IPv4
"52.46.0.0/18", // CloudFront IPv4
"2600:9000::/28", // CloudFront IPv6
))
// Behind a known number of proxies with dynamic IPs:
// r.Use(middleware.ClientIPFromXFFTrustedProxies(2))
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
clientIP := middleware.GetClientIP(r.Context()) // for logs, rate-limit keys, etc.
_ = clientIP
})These middlewares never mutate r.RemoteAddr. They store a normalizednetip.Addr in the request context โ IPv4-mapped IPv6 (::ffff:a.b.c.d)
is folded to plain IPv4, and IPv6 zone identifiers carried in headers are
stripped, so one logical client maps to a single canonical key for logs,
rate limits, and ACLs.
For ClientIPFromXFFTrustedProxies, numTrustedProxies is the total proxy
hops between client and server. Prefer ClientIPFromXFF with explicit CIDRs
when you can โ CIDR-based trust cannot off-by-one. See the godoc for a
deployment recipe and a verify checklist.
See the per-function godoc for the full semantics of each middleware, and
adam-p's "The perils of the 'real' client IP"
for the underlying threat model.
Extra middlewares & packages
Please see https://github.com/go-chi for additional packages.
| package | description |
|---|---|
| cors | Cross-origin resource sharing (CORS) |
| docgen | Print chi.Router routes at runtime |
| jwtauth | JWT authentication |
| hostrouter | Domain/host based request routing |
| httplog | Small but powerful structured HTTP request logging |
| httprate | HTTP request rate limiter |
| httptracer | HTTP request performance tracing library |
| httpvcr | Write deterministic tests for external sources |
| stampede | HTTP request coalescer |
context?
context is a tiny pkg that provides simple interface to signal context across call stacks
and goroutines. It was originally written by Sameer Ajmani
and is available in stdlib since go1.7.
Learn more at https://blog.golang.org/context
and..
Benchmarks
The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark
Results as of Nov 29, 2020 with Go 1.15.5 on Linux AMD 3950x
BenchmarkChi_Param 3075895 384 ns/op 400 B/op 2 allocs/op
BenchmarkChi_Param5 2116603 566 ns/op 400 B/op 2 allocs/op
BenchmarkChi_Param20 964117 1227 ns/op 400 B/op 2 allocs/op
BenchmarkChi_ParamWrite 2863413 420 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GithubStatic 3045488 395 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GithubParam 2204115 540 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GithubAll 10000 113811 ns/op 81203 B/op 406 allocs/op
BenchmarkChi_GPlusStatic 3337485 359 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GPlusParam 2825853 423 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GPlus2Params 2471697 483 ns/op 400 B/op 2 allocs/op
BenchmarkChi_GPlusAll 194220 5950 ns/op 5200 B/op 26 allocs/op
BenchmarkChi_ParseStatic 3365324 356 ns/op 400 B/op 2 allocs/op
BenchmarkChi_ParseParam 2976614 404 ns/op 400 B/op 2 allocs/op
BenchmarkChi_Parse2Params 2638084 439 ns/op 400 B/op 2 allocs/op
BenchmarkChi_ParseAll 109567 11295 ns/op 10400 B/op 52 allocs/op
BenchmarkChi_StaticAll 16846 71308 ns/op 62802 B/op 314 allocs/opComparison with other routers: https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc
NOTE: the allocs in the benchmark above are from the calls to http.Request'sWithContext(context.Context) method that clones the http.Request, sets the Context()
on the duplicated (alloc'd) request and returns it the new request object. This is just
how setting context on a request in Go works.
Credits
- Carl Jackson for https://github.com/zenazn/goji
- Armon Dadgar for https://github.com/armon/go-radix
- Contributions: @VojtechVitek
We'll be more than happy to see your contributions!
Beyond REST
chi is just a http router that lets you decompose request handling into many smaller layers.
Many companies use chi to write REST services for their public APIs. But, REST is just a convention
for managing state via HTTP, and there's a lot of other pieces required to write a complete client-server
system or network of microservices.
Looking beyond REST, I also recommend some newer works in the field:
- webrpc - Web-focused RPC client+server framework with code-gen
- gRPC - Google's RPC framework via protobufs
- graphql - Declarative query language
- NATS - lightweight pub-sub
License
Copyright (c) 2015-present Peter Kieltyka
Licensed under MIT License
File: _examples/README.md
chi examples
- custom-handler - Use a custom handler function signature
- custom-method - Add a custom HTTP method
- fileserver - Easily serve static files
- graceful - Graceful context signaling and server shutdown
- hello-world - Hello World!
- limits - Timeouts and Throttling
- logging - Easy structured logging for any backend
- rest - REST APIs made easy, productive and maintainable
- router-walk - Print to stdout a router's routes
- todos-resource - Struct routers/handlers, an example of another code layout style
- versions - Demo of
chi/rendersubpkg - pathvalue - Demonstrates
PathValueusage for retrieving URL parameters
Usage
go get -v -d -u ./...- fetch example depscd <example>/ie.cd rest/go run *.go- note, example services run on port 3333- Open another terminal and use curl to send some requests to your example service,
curl -v http://localhost:3333/ - Read
/main.go source to learn how service works and read comments for usage
2. Official Technical Reference & Guides (go-chi/docs)
chi
๐ Hi, Let's Get You Started With chi
chi is a lightweight, idiomatic and composable router for building Go HTTP services. It's
especially good at helping you write large REST API services that are kept maintainable as your
project grows and changes. chi is built on the new context package introduced in Go 1.7 to
handle signaling, cancelation and request-scoped values across a handler chain.
The focus of the project has been to seek out an elegant and comfortable design for writing
REST API servers, written during the development of the Pressly API service that powers our
public API service, which in turn powers all of our client-side applications.
The key considerations of chi's design are: project structure, maintainability, standard http
handlers (stdlib-only), developer productivity, and deconstructing a large system into many small
parts. The core router github.com/go-chi/chi is quite small (less than 1000 LOC), but we've also
included some useful/optional subpackages: middleware, render
and docgen. We hope you enjoy it too!
Features
- Lightweight - cloc'd in ~1000 LOC for the chi router
- Fast - yes, see benchmarks
- 100% compatible with net/http - use any http or middleware pkg in the ecosystem that is also compatible with
net/http - Designed for modular/composable APIs - middlewares, inline middlewares, route groups and sub-router mounting
- Context control - built on new
contextpackage, providing value chaining, cancellations and timeouts - Robust - in production at Pressly, CloudFlare, Heroku, 99Designs, and many others (see discussion)
- Doc generation -
docgenauto-generates routing documentation from your source to JSON or Markdown - Go.mod support - as of v5, go.mod support (see CHANGELOG)
- No external dependencies - plain ol' Go stdlib + net/http
Examples
See examples for a variety of examples.
Contributing to these docs
This documentation is a static docsify site โ there
is no build step. To preview your changes locally, serve this directory with
the docsify CLI (requires Node.js):
npx docsify-cli serve .Then open http://localhost:3000. Pages live-reload as you edit the markdown
files. Note that docsify itself is loaded from a CDN, so an internet
connection is required even when serving locally.
License
Copyright (c) 2015-present Peter Kieltyka
Licensed under MIT License