Getting Started #
In this tutorial you are going to learn how to use Chi router to create HTTP endpoints in Go. We will also take a look at using middleware in Chi and grouping routes.
Installing Chi #
Setup a new Go project using go mod init ...
and install Chi using the following command.
go get -u github.com/go-chi/chi/v5
Writing HTTP Endpoints #
A good thing about Chi is its ease of use and minimal API. Creating an endpoint is as simple as follows.
- We start by creating a new router using
chi.NewRouter()
router := chi.NewRouter()
- We register a new
GET
endpoint with the path/
.
router.Get("/", func(writer http.ResponseWriter, request *http.Request) {
_, err := writer.Write([]byte("Hello World"))
if err != nil {
log.Println(err)
}
})
Notice that chi takes the standard http.HandlerFunc
as the handler function for an endpoint. FYI, http.HandlerFunc
underlying type declaration is type HandlerFunc func(ResponseWriter, *Request)
.
- Chi router implements
http.Handler
interface so you can directly use it withhttp.ListenAndServe
.
err := http.ListenAndServe(":3000", router)
if err != nil {
log.Println(err)
}
Here is the full working example.
package main
import (
"github.com/go-chi/chi/v5"
"log"
"net/http"
)
func main() {
// 1. Create a new router
router := chi.NewRouter()
// 2. Register an endpoint
router.Get("/", func(writer http.ResponseWriter, request *http.Request) {
_, err := writer.Write([]byte("Hello World"))
if err != nil {
log.Println(err)
}
})
// 3. Use router to start the server
err := http.ListenAndServe(":3000", router)
if err != nil {
log.Println(err)
}
}
HTTP Methods #
Writing endpoints with other HTTP methods (POST
, PUT
, DELETE
etc) is exactly like writing the GET
request above, chi provides functions for all of them.
router.Put()
router.Post()
router.Delete()
router.Patch()
For endpoints that have a request body, you would parse the body just like you parse with regular Go http package.
Path Parameters #
chi provides a function (chi.URLParam
) to get path parameters that are defined using curly brackets { }
.
router.Get("/user/{username}", func(writer http.ResponseWriter, request *http.Request) {
username := chi.URLParam(request, "username") // 👈 getting path param
_, err := writer.Write([]byte("Hello " + username))
if err != nil {
log.Println(err)
}
})
In this example we define a GET endpoint that has a path parameter username
, we call chi.URLParam(request, "username")
to get the value of the path parameter.
To get query params from a request you can use the inbuilt Go functionality request.URL.Query()
.
Middleware with Chi #
Chi lets you to easily add middleware to the router using the use
function. The use
function takes in another function as a parameter that takes in a http.Handler and returns a http.Handler as well (http.Handler is just an interface with a single function ServeHTTP(ResponseWriter, *Request)
).
Here is how you would write a middleware to log each request's path.
// 1. Write a middleware
func Logger(handler http.Handler) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
log.Println(request.URL.Path)
handler.ServeHTTP(writer, request)
})
}
// 2. Register with router
router.Use(Logger)
Here is the full example with logging middleware.
package main
import (
"github.com/go-chi/chi/v5"
"log"
"net/http"
)
func main() {
router := chi.NewRouter()
router.Use(Logger) // 👈 register middleware with router
router.Get("/", func(writer http.ResponseWriter, request *http.Request) {
_, err := writer.Write([]byte("Hello World"))
if err != nil {
log.Println(err)
}
})
err := http.ListenAndServe(":3000", router)
if err != nil {
log.Println(err)
}
}
// 👇 a logging middleware
func Logger(handler http.Handler) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
log.Println(request.URL.Path)
handler.ServeHTTP(writer, request)
})
}
Chi provides a wide range of middlewares built into the package itself, you can find the complete list here.
Using the inbuilt logging middleware from Chi:
package main
import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware" // 👈 import middleware package
"log"
"net/http"
)
func main() {
router := chi.NewRouter()
router.Use(middleware.Logger) // 👈 use the inbuilt logging middleware
...
}
Grouping Routes #
Chi provides a couple of ways to group routes.
You can use the chi.Route
function to create sub-routes.
router.Route("/users", func(r chi.Router) {
r.Get("/{id}", getUser)
r.Post("/", createUser)
r.Put("/deactivate", deactivateUser)
})
This will add 3 routes to the router GET /users/{id}
POST /users/
and PUT /users/deactivate
.
You can group routes using chi.Group
which allows you to apply middleware to only grouped routes.
router.Post("/login", login)
router.Post("/signup", login)
// Apply auth middleware to only `GET /users/{id}`
router.Group(func(r chi.Router) {
r.Use(AuthMiddleware)
r.Get("/users/{id}")
})
Thank you for reading this article 🙏 I hope you learned something new, highly encourage you to read the chi documentation.