You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Wijnand Modderman-Lenstra 2298f60ac9 More sugar 3 years ago
_examples Update example 3 years ago
docgen Ported middlewares 3 years ago
header Rewitten to 3 years ago
middleware More sugar 3 years ago
.drone.yml Be verbose 3 years ago
.gitignore Ensure a route context is available 3 years ago
.travis.yml Travis: Build examples 3 years ago Update README, etc 3 years ago Add Travis widget to README 3 years ago
LICENSE Rewitten to 3 years ago Ported middlewares 3 years ago
chain.go Ported middlewares 3 years ago
content.go Versioned package; Cleanups 3 years ago
context.go Added NotFound 3 years ago
mux.go Retrieve context from request context 3 years ago
mux_test.go Versioned package; Cleanups 3 years ago
param.go Versioned package; Cleanups 3 years ago
phi.go Ported middlewares 3 years ago
register.go Rewitten to 3 years ago
render.go Convert Context to interface 3 years ago
tree.go Versioned package; Cleanups 3 years ago
tree_test.go Convert Context to interface 3 years ago


GoDoc Widget Drone Widget Codebeat Widget

phi is a lightweight, idiomatic and composable router for building Go 1.7+ HTTP services. It’s especially good at helping you write large REST API services that are kept maintainable as your project grows and changes. phi 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 and 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 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!


  • Lightweight - cloc’d in <1000 LOC for the phi router
  • Fast - yes, see benchmarks
  • Designed for modular/composable APIs - middlewares, inline middlewares, route groups and subrouter mounting
  • Context control - built on new context package, providing value chaining, cancelations and timeouts
  • Robust - tested / used in production at, and many others
  • Doc generation - docgen auto-generates routing documentation from your source to JSON or Markdown
  • No external dependencies - plain ol’ Go 1.7+ stdlib + net/http



  • rest - REST APIs made easy, productive and maintainable
  • limits - Timeouts and Throttling
  • todos-resource - Struct routers/handlers, an example of another code layout style
  • versions - Demo of chi/render subpkg
  • fileserver - Easily serve static files


Here is a little preview of how routing looks like with phi. Also take a look at the generated routing docs in JSON (routes.json) and in Markdown (

import (
  phi ""

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

  // A good base middleware stack

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

  // 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) {

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

    r.Route("/:articleID", func(r chi.Router) {
      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 phi.Handler) phi.Handler {
  return http.HandlerFunc(func(ctx *phi.Context) {
    articleID := ctx.Param("articleID")
    article, err := dbGetArticle(articleID)
    if err != nil {
    ctx.SetValue("article", article)

func getArticle(ctx *phi.Context) {
  article, ok := ctx.Value("article").(*Article)
  if !ok {
    ctx.Error(nil, 422)
  ctx.Write([]byte(fmt.Sprintf("title:%s", article.Title)))

// A completely separate router for administrator routes
func adminRouter() phi.Handler {
  r := phi.NewRouter()
  r.Get("/", adminIndex)
  r.Get("/accounts", adminListAccounts)
  return r

func AdminOnly(next phi.Handler) phi.Handler {
  return http.HandlerFunc(func(ctx *phi.Context) {
    perm, ok := ctx.Value("acl.permission").(YourPermissionType)
    if !ok || !perm.IsAdmin() {
      ctx.Error(nil, 403)

Router design

Phi’s router is based on Chi’s router.

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

// Router consisting of the core routing methods used by phi's Mux.
type Router interface {

	// Use appends one of more middlewares onto the Router stack.
	Use(middlewares ...func(Handler) Handler)

	// With adds inline middlewares for an endpoint handler.
	With(middlewares ...func(Handler) Handler) Router

	// Group adds a new inline-Router along the current routing
	// path, with a fresh middleware stack for the inline-Router.
	Group(fn func(r Router)) Router

	// Route mounts a sub-Router along a `pattern`` string.
	Route(pattern string, fn func(r Router)) Router

	// Mount attaches another Handler along ./pattern/*
	Mount(pattern string, h Handler)

	// Handle and HandleFunc adds routes for `pattern` that matches
	// all HTTP methods.
	Handle(pattern string, h Handler)
	HandleFunc(pattern string, h HandlerFunc)
  HTTPHandle(pattern string, h http.Handler)
  HTTPHandleFunc(pattern string, h http.HandlerFunc)

	// HTTP-method routing along `pattern`
	Connect(pattern string, h HandlerFunc)
	Delete(pattern string, h HandlerFunc)
	Get(pattern string, h HandlerFunc)
	Head(pattern string, h HandlerFunc)
	Options(pattern string, h HandlerFunc)
	Patch(pattern string, h HandlerFunc)
	Post(pattern string, h HandlerFunc)
	Put(pattern string, h HandlerFunc)
	Trace(pattern string, h HandlerFunc)

	// NotFound defines a handler to respond whenever a route could
	// not be found.
	NotFound(h HandlerFunc)

// Routes interface adds two methods for router traversal, which is also
// used by the `docgen` subpackage to generation documentation for Routers.
type Routes interface {
	// Routes returns the routing tree in an easily traversable structure.
	Routes() []Route

	// Middlewares returns the list of middlewares in use by the router.
	Middlewares() Middlewares

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

Middleware handlers

// HTTP middleware setting a value on the request context
func Middleware(next Handler) Handler {
  return http.HandlerFunc(func(ctx *phi.Context) {
    ctx.SetValue("user", "123")

Request handlers

// HTTP handler accessing data from the request context.
func Handler(ctx *phi.Context) {
  user := ctx.Value("user").(string)
  ctx.Write([]byte(fmt.Sprintf("hi %s", user)))
// HTTP handler accessing the url routing parameters.
func CtxHandler(ctx *phi.Context) {
  userID := ctx.Param("userID") // from a route like /users/:userID

  key := ctx.Value("key").(string)

  ctx.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key)))


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.
Compress Gzip compression for clients that accept compressed responses.
Profiler Easily attach net/http/pprof to your routers.
Slashes Strip and redirect slashes on routing paths.
WithValue Short-hand middleware to set a key/value on the request context.



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


Copyright © 2016-present maze

Licensed under MIT License