README

chi

GoDoc Widget Travis Widget

chi is a small, fast and expressive router / mux for Go HTTP services built with net/context.

Chi encourages writing services by composing small handlers and middlewares with many or few routes. Each middleware is like a layer of an onion connected through a consistent interface (http.Handler or chi.Handler) and a context.Context argument that flows down the layers during a request's lifecycle.

In order to get the most out of this pattern, chi's routing methods (Get, Post, Handle, Mount, etc.) support inline middlewares, middleware groups, and mounting (composing) any chi router to another - a bushel of onions. We've designed the Pressly API (150+ routes/handlers) exactly like this and its scaled very well.

alt tag

Features

  • Lightweight - cloc'd in <1000 LOC for the chi router
  • Fast - yes, see benchmarks
  • Zero allocations - no GC pressure during routing
  • Designed for modular/composable APIs - middlewares, inline middleware groups/chains, and subrouter mounting
  • Context control - built on net/context with value chaining, deadlines and timeouts
  • Robust - tested / used in production

Router design

Chi's router is based on a kind of Patricia Radix trie. Built on top of the tree is the Router interface:

// Register a middleware handler (or few) on the middleware stack
Use(middlewares ...interface{})

// Register a new middleware stack
Group(fn func(r Router)) Router

// Mount an inline sub-router
Route(pattern string, fn func(r Router)) Router

// Mount a sub-router
Mount(pattern string, handlers ...interface{})

// Register routing handler for all http methods
Handle(pattern string, handlers ...interface{})

// Register routing handler for CONNECT http method
Connect(pattern string, handlers ...interface{})

// Register routing handler for HEAD http method
Head(pattern string, handlers ...interface{})

// Register routing handler for GET http method
Get(pattern string, handlers ...interface{})

// Register routing handler for POST http method
Post(pattern string, handlers ...interface{})

// Register routing handler for PUT http method
Put(pattern string, handlers ...interface{})

// Register routing handler for PATCH http method
Patch(pattern string, handlers ...interface{})

// Register routing handler for DELETE http method
Delete(pattern string, handlers ...interface{})

// Register routing handler for TRACE http method
Trace(pattern string, handlers ...interface{})

// Register routing handler for OPTIONS http method
Options(pattern string, handlers ...interface{})

Each routing method accepts a URL pattern and chain of handlers. The URL pattern supports named params (ie. /users/:userID) and wildcards (ie. /admin/*).

The handlers argument can be a single request handler, or a chain of middleware handlers, followed by a request handler. The request handler is required, and must be the last argument.

We lose type checking of the handlers, but that'll be resolved sometime in the future, we hope, when Go's stdlib supports net/context in net/http. For now, chi checks the types at runtime and panics in case of a mismatch.

The supported handlers are as follows..

Middleware handlers
// Standard HTTP middleware. Compatible and friendly for when a request context isn't needed.
func StdMiddleware(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    next.ServeHTTP(w, r)
  })
}
// net/context HTTP middleware. Useful for signaling to stop processing, adding a timeout,
// cancellation, or passing data down the middleware chain.
func CtxMiddleware(next chi.Handler) chi.Handler {
  return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
    ctx = context.WithValue(ctx, "key", "value")
    next.ServeHTTPC(ctx, w, r)
  })
}
Request handlers
// Standard HTTP handler
func StdHandler(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("hi"))
}
// net/context HTTP request handler
func CtxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
  userID := chi.URLParam(ctx, "userID") // from a route like /users/:userID
  key := ctx.Value("key").(string)
  w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key)))
}

net/context?

net/context is a tiny library written by Sameer Ajmani that provides a simple interface to signal context across call stacks and goroutines.

Learn more at https://blog.golang.org/context

and..

Examples

Examples:

  • simple - The power of handler composability
  • rest - REST apis made easy; includes a simple JSON responder

Preview:

import (
  //...
  "github.com/pressly/chi"
  "github.com/pressly/chi/middleware"
  "golang.org/x/net/context"
)

func main() {
  r := chi.NewRouter()

  // A good base middleware stack
  r.Use(middleware.RequestID)
  r.Use(middleware.RealIP)
  r.Use(middleware.Logger)
  r.Use(middleware.Recoverer)

  // When a client closes their connection midway through a request, the
  // http.CloseNotifier will cancel the request context (ctx).
  r.Use(middleware.CloseNotify)

  // Set a timeout value on the request context (ctx), that will signal
  // through ctx.Done() that the request has timed out and further
  // processing should be stopped.
  r.Use(middleware.Timeout(60 * time.Second))

  r.Get("/", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hi"))
  })

  // RESTy routes for "articles" resource
  r.Route("/articles", func(r chi.Router) {
    r.Get("/", paginate, listArticles)  // GET /articles
    r.Post("/", createArticle)          // POST /articles

    r.Route("/:articleID", func(r chi.Router) {
      r.Use(ArticleCtx)
      r.Get("/", getArticle)            // GET /articles/123
      r.Put("/", updateArticle)         // PUT /articles/123
      r.Delete("/", deleteArticle)      // DELETE /articles/123
    })
  })

  // Mount the admin sub-router
  r.Mount("/admin", adminRouter())

  http.ListenAndServe(":3333", r)
}

func ArticleCtx(next chi.Handler) chi.Handler {
  return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
    articleID := chi.URLParam(ctx, "articleID")
    article, err := dbGetArticle(articleID)
    if err != nil {
      http.Error(w, http.StatusText(404), 404)
      return
    }
    ctx = context.WithValue(ctx, "article", article)
    next.ServeHTTPC(ctx, w, r)
  })
}

func getArticle(ctx context.Context, w http.ResponseWriter, r *http.Request) {
  article, ok := ctx.Value("article").(*Article)
  if !ok {
    http.Error(w, http.StatusText(422), 422)
    return
  }
  w.Write([]byte(fmt.Sprintf("title:%s", article.Title)))
}

// A completely separate router for administrator routes
func adminRouter() chi.Router {
  r := chi.NewRouter()
  r.Use(AdminOnly)
  r.Get("/", adminIndex)
  r.Get("/accounts", adminListAccounts)
  return r
}

func AdminOnly(next chi.Handler) chi.Handler {
  return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
    perm, ok := ctx.Value("acl.permission").(YourPermissionType)
    if !ok || !perm.IsAdmin() {
      http.Error(w, http.StatusText(403), 403)
      return
    }
    next.ServeHTTPC(ctx, w, r)
  })
}

Middlewares

Chi comes equipped with an optional middleware package, providing:


Middleware Description
RequestID Injects a request ID into the context of each request.
RealIP Sets a http.Request's RemoteAddr to either X-Forwarded-For or X-Real-IP.
Logger Logs the start and end of each request with the elapsed processing time.
Recoverer Gracefully absorb panics and prints the stack trace.
NoCache Sets response headers to prevent clients from caching.
CloseNotify Signals to the request context when a client has closed their connection.
Timeout Signals to the request context when the timeout deadline is reached.
Throttle Puts a ceiling on the number of concurrent requests.

Other middlewares:

please submit a PR if you'd like to include a link to a chi middleware

Future

We're hoping that by Go 1.7 (in 2016), net/context will be in the Go stdlib and net/http will support context.Context. You'll notice that chi.Handler and http.Handler are very similar and the middleware signatures follow the same structure. One day chi.Handler will be deprecated and the router will live on just as it is without any dependencies beyond stdlib. And... then, we have infinitely more middlewares to compose from the community!!

See discussions:

Benchmarks

The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark

BenchmarkChi_Param            10000000         128 ns/op         0 B/op        0 allocs/op
BenchmarkChi_Param5            5000000         303 ns/op         0 B/op        0 allocs/op
BenchmarkChi_Param20           1000000        1064 ns/op         0 B/op        0 allocs/op
BenchmarkChi_ParamWrite       10000000         181 ns/op         0 B/op        0 allocs/op
BenchmarkChi_GithubStatic     10000000         193 ns/op         0 B/op        0 allocs/op
BenchmarkChi_GithubParam       5000000         344 ns/op         0 B/op        0 allocs/op
BenchmarkChi_GithubAll           20000       63100 ns/op         0 B/op        0 allocs/op
BenchmarkChi_GPlusStatic      20000000         124 ns/op         0 B/op        0 allocs/op
BenchmarkChi_GPlusParam       10000000         172 ns/op         0 B/op        0 allocs/op
BenchmarkChi_GPlus2Params      5000000         232 ns/op         0 B/op        0 allocs/op
BenchmarkChi_GPlusAll           500000        2684 ns/op         0 B/op        0 allocs/op
BenchmarkChi_ParseStatic      10000000         135 ns/op         0 B/op        0 allocs/op
BenchmarkChi_ParseParam       10000000         154 ns/op         0 B/op        0 allocs/op
BenchmarkChi_Parse2Params     10000000         192 ns/op         0 B/op        0 allocs/op
BenchmarkChi_ParseAll           300000        4637 ns/op         0 B/op        0 allocs/op
BenchmarkChi_StaticAll           50000       37583 ns/op         0 B/op        0 allocs/op

Credits

We'll be more than happy to see your contributions!

License

Copyright (c) 2015-2016 Peter Kieltyka

Licensed under MIT License

Expand ▾ Collapse ▴

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func URLParam

func URLParam(ctx context.Context, key string) string

    URLParam returns a url paramter from the routing context.

    Types

    type Context

    type Context struct {
    	context.Context
    
    	// URL parameter key and values
    	Params params
    
    	// Routing path override used by subrouters
    	RoutePath string
    }

      A Context is the default routing context set on the root node of a request context to track URL parameters and an optional routing path.

      func NewContext

      func NewContext() *Context

        NewContext returns a new routing context object.

        func RouteContext

        func RouteContext(ctx context.Context) *Context

          RouteContext returns chi's routing context object that holds url params and a routing path for subrouters.

          type Handler

          type Handler interface {
          	ServeHTTPC(context.Context, http.ResponseWriter, *http.Request)
          }

            Handler is like net/http's http.Handler, but also includes a mechanism for serving requests with a context.

            type HandlerFunc

            type HandlerFunc func(context.Context, http.ResponseWriter, *http.Request)

              HandlerFunc is like net/http's http.HandlerFunc, but supports a context object.

              func (HandlerFunc) ServeHTTP

              func (h HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request)

                ServeHTTP provides compatibility with http.Handler.

                func (HandlerFunc) ServeHTTPC

                func (h HandlerFunc) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request)

                  ServeHTTPC wraps ServeHTTP with a context parameter.

                  type Mux

                  type Mux struct {
                  	// contains filtered or unexported fields
                  }

                    A Mux is a simple HTTP route multiplexer that parses a request path, records any URL params, and executes an end handler. It implements the http.Handler interface and is friendly with the standard library.

                    Mux is designed to be fast, minimal and offer a powerful API for building modular HTTP services with a large set of handlers. It's particularly useful for writing large REST API services that break a handler into many smaller parts composed of middlewares and end handlers.

                    func NewMux

                    func NewMux() *Mux

                      NewMux returns a new Mux object with an optional parent context.

                      func NewRouter

                      func NewRouter() *Mux

                        NewRouter returns a new Mux object that implements the Router interface. It accepts an optional parent context.Context argument used by all request contexts useful for signaling a server shutdown.

                        func (*Mux) Connect

                        func (mx *Mux) Connect(pattern string, handlers ...interface{})

                          Connect adds a route that matches a CONNECT http method and the `pattern` for the `handlers` chain.

                          func (*Mux) Delete

                          func (mx *Mux) Delete(pattern string, handlers ...interface{})

                            Delete adds a route that matches a DELETE http method and the `pattern` for the `handlers` chain.

                            func (*Mux) FileServer

                            func (mx *Mux) FileServer(path string, root http.FileSystem)

                              FileServer conveniently sets up a http.FileServer handler to serve static files from a http.FileSystem.

                              func (*Mux) Get

                              func (mx *Mux) Get(pattern string, handlers ...interface{})

                                Get adds a route that matches a GET http method and the `pattern` for the `handlers` chain.

                                func (*Mux) Group

                                func (mx *Mux) Group(fn func(r Router)) Router

                                  Group creates a new inline-Mux with a fresh middleware stack. It's useful for a group of handlers along the same routing path that use the same middleware(s). See _examples/ for an example usage.

                                  func (*Mux) Handle

                                  func (mx *Mux) Handle(pattern string, handlers ...interface{})

                                    Handle adds a route for all http methods that match the `pattern` for the `handlers` chain.

                                    func (*Mux) Head

                                    func (mx *Mux) Head(pattern string, handlers ...interface{})

                                      Head adds a route that matches a HEAD http method and the `pattern` for the `handlers` chain.

                                      func (*Mux) Mount

                                      func (mx *Mux) Mount(path string, handlers ...interface{})

                                        Mount attaches another mux as a subrouter along a routing path. It's very useful to split up a large API as many independent routers and compose them as a single service using Mount. See _examples/ for example usage.

                                        func (*Mux) NotFound

                                        func (mx *Mux) NotFound(h HandlerFunc)

                                          NotFound sets a custom http.HandlerFunc for missing routes on the treeRouter.

                                          func (*Mux) Options

                                          func (mx *Mux) Options(pattern string, handlers ...interface{})

                                            Options adds a route that matches a OPTIONS http method and the `pattern` for the `handlers` chain.

                                            func (*Mux) Patch

                                            func (mx *Mux) Patch(pattern string, handlers ...interface{})

                                              Patch adds a route that matches a PATCH http method and the `pattern` for the `handlers` chain.

                                              func (*Mux) Post

                                              func (mx *Mux) Post(pattern string, handlers ...interface{})

                                                Post adds a route that matches a POST http method and the `pattern` for the `handlers` chain.

                                                func (*Mux) Put

                                                func (mx *Mux) Put(pattern string, handlers ...interface{})

                                                  Put adds a route that matches a PUT http method and the `pattern` for the `handlers` chain.

                                                  func (*Mux) Route

                                                  func (mx *Mux) Route(pattern string, fn func(r Router)) Router

                                                    Route creates a new Mux with a fresh middleware stack and mounts it along the `pattern`. This is very simiular to the Group, but attaches the group along a new routing path. See _examples/ for example usage.

                                                    func (*Mux) ServeHTTP

                                                    func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request)

                                                      ServeHTTP is the single method of the http.Handler interface that makes Mux interoperable with the standard library. It uses a sync.Pool to get and reuse routing contexts for each request.

                                                      func (*Mux) ServeHTTPC

                                                      func (mx *Mux) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request)

                                                        ServeHTTPC is chi's Handler method that adds a context.Context argument to the standard ServeHTTP handler function.

                                                        func (*Mux) Trace

                                                        func (mx *Mux) Trace(pattern string, handlers ...interface{})

                                                          Trace adds a route that matches a TRACE http method and the `pattern` for the `handlers` chain.

                                                          func (*Mux) Use

                                                          func (mx *Mux) Use(mws ...interface{})

                                                            Use appends a middleware handler to the Mux middleware stack.

                                                            type Router

                                                            type Router interface {
                                                            	http.Handler
                                                            	Handler
                                                            
                                                            	Use(middlewares ...interface{})
                                                            	Group(fn func(r Router)) Router
                                                            	Route(pattern string, fn func(r Router)) Router
                                                            	Mount(pattern string, handlers ...interface{})
                                                            
                                                            	Handle(pattern string, handlers ...interface{})
                                                            	NotFound(h HandlerFunc)
                                                            
                                                            	Connect(pattern string, handlers ...interface{})
                                                            	Head(pattern string, handlers ...interface{})
                                                            	Get(pattern string, handlers ...interface{})
                                                            	Post(pattern string, handlers ...interface{})
                                                            	Put(pattern string, handlers ...interface{})
                                                            	Patch(pattern string, handlers ...interface{})
                                                            	Delete(pattern string, handlers ...interface{})
                                                            	Trace(pattern string, handlers ...interface{})
                                                            	Options(pattern string, handlers ...interface{})
                                                            }

                                                              A Router consisting of the core routing methods used by chi's Mux.

                                                              NOTE, the plan: hopefully once net/context makes it into the stdlib and net/http supports a request context, we will remove the chi.Handler interface, and the Router argument types will be http.Handler instead of interface{}.

                                                              type WalkFn

                                                              type WalkFn func(path string, handler Handler) bool

                                                                WalkFn is used when walking the tree. Takes a key and value, returning if iteration should be terminated.

                                                                Directories

                                                                Path Synopsis
                                                                _examples