Table of Contents
golang chi is a compact, idiomatic, lightweight router for creating Go HTTP services. It specializes at assisting you in creating sizable REST API services that can be maintained as your project expands and evolves. chi is designed to handle signaling, cancellations, and request-scoped data throughout a handler chain and is based on the new context package introduced in Go 1.7. chi requires Go 1.10 or newer.
Golang Chi Installation and Features
Run the below command to install the package:
$ go get -u github.com/go-chi/chi/v5
Features
- Lightweight
- Fast - benchmarks
- 100% compatible with net/http - use all http or middleware pkg 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Â
context
package - Robust - in production with Cloudflare, Heroku,... and many others (see discussion)
- Doc generation -Â
docgen
auto-generates routing documentation from source to JSON or Markdown - Go.mod support - as of v5, go.mod support (see CHANGELOG)
- No external dependencies
Example 1: Create a simple HTTP router using chi
In this example, we will create a simple HTTP router with some middleware using chi
:
logger
: Logger is a middleware that logs the start and end of each request, along with some useful data about what was requested, what the response status was, and how long it took to return. When standard output is a TTY, Logger will print in color, otherwise it will print in black and white. Logger prints a request ID if one is provided.
requestID
: RequestID is a middleware that injects a request ID into the context of each request. A request ID is a string of the form "host.example.com/random-0001", where "random" is a base62 random string that uniquely identifies this go process, and where the last number is an atomically incremented request counter.
recover
: Recoverer is a middleware that recovers from panics, logs the panic (and a backtrace), and returns a HTTP 500 (Internal Server Error) status if possible. Recoverer prints a request ID if one is provided.
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
rout := chi.NewRouter()
rout.Use(middleware.RequestID)
rout.Use(middleware.Logger)
rout.Use(middleware.Recoverer)
rout.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello GolinuxCloud members!"))
})
http.ListenAndServe(":8080", rout)
}
Output:
You can see that with the above middleware, some information about the request were printed out.
Example 2: Create a HTTP router with custom method
In the below example, we will create a http server which accepts some custom method:
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func init() {
chi.RegisterMethod("PUT")
chi.RegisterMethod("POST")
}
func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello golinuxcloud members!"))
})
r.MethodFunc("PUT", "/put", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("this is put method"))
})
r.MethodFunc("POST", "/post", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("this is post method"))
})
r.HandleFunc("/all", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("capturing all standard http methods"))
})
http.ListenAndServe(":8080", r)
}
Output:
Example 3: Create a HTTP router with custom handler
The below examples create a custom handler for request. The request will send id parameter, if invalid or empty id, return an error. If id is valid, find the person with that id, return the person's name:
package main
import (
"errors"
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
)
type Handler func(w http.ResponseWriter, r *http.Request) error
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := h(w, r); err != nil {
// handle returned error here.
w.WriteHeader(500)
w.Write([]byte("empty or invalid id"))
}
}
type Person struct {
id int
name string
character string
}
var listPerson = []Person{
Person{
id: 1,
name: "Slack",
character: "Harry Potter",
},
Person{
id: 2,
name: "Anna",
character: "John Weasley",
},
Person{
id: 3,
name: "Bob",
character: "Malfoy",
},
}
func main() {
r := chi.NewRouter()
r.Method("GET", "/", Handler(customHandler))
http.ListenAndServe(":8080", r)
}
func customHandler(w http.ResponseWriter, r *http.Request) error {
idQuery := r.URL.Query().Get("id")
if idQuery == "" {
return errors.New(idQuery)
}
id, err := strconv.Atoi(idQuery)
if err != nil || id > 3 || id < 0 {
return errors.New(idQuery)
}
w.Write([]byte(listPerson[id].name))
return nil
}
Example 4: Build a file server using chi
The server code directory structure:
C:.
| gochi.go //server
|
\---data //data folder
file1.txt
file2.txt
In the below example, the user will send a request to read a file from server, if file exists, return the content, if not return an error:
package main
import (
"net/http"
"os"
"path/filepath"
"strings"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
route := chi.NewRouter()
route.Use(middleware.Logger)
// Index handler
route.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome to file server"))
})
// Create a route along /files that will serve contents from the ./data/ folder.
workDir, _ := os.Getwd()
filesDir := http.Dir(filepath.Join(workDir, "data"))
FileServer(route, "/files", filesDir)
http.ListenAndServe(":8080", route)
}
func FileServer(r chi.Router, path string, root http.FileSystem) {
if strings.ContainsAny(path, "{}*") {
panic("FileServer does not permit any URL parameters.")
}
if path != "/" && path[len(path)-1] != '/' {
r.Get(path, http.RedirectHandler(path+"/", 301).ServeHTTP)
path += "/"
}
path += "*"
r.Get(path, func(w http.ResponseWriter, r *http.Request) {
rctx := chi.RouteContext(r.Context())
pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*")
fs := http.StripPrefix(pathPrefix, http.FileServer(root))
fs.ServeHTTP(w, r)
})
}
Output:
Summary
chi is simply an HTTP router that allows you to break down request handling into many smaller layers. Many businesses use chi to create REST services for public APIs. However, REST is just a convention for managing state via HTTP, and there are many other pieces that must be written in order to create a complete client-server system or network of microservices.
References
https://pkg.go.dev/github.com/go-chi/chi
https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods