When it comes to building web applications in Go, having a robust and flexible router is essential. Chi is a lightweight, idiomatic, and composable router for building Go HTTP services. In this blog post, we’ll explore the features of Chi and how to use it to create a simple web application.

Why Chi?

Chi stands out due to its simplicity and performance. It provides:

Lightweight design: Chi has a small footprint, making it ideal for microservices.
Composability: Its middleware stack is highly composable.
Context support: Chi uses Go’s context.Context for request-scoped data.
API versioning: Easy to create versioned APIs.
Getting Started
Installation
First, install Chi using Go modules:

bash

go get github.com/go-chi/chi/v5
Basic Example
Let’s start with a basic example to see Chi in action.

package main
import (
    "net/http"
    "github.com/go-chi/chi/v5"
    "github.com/go-chi/chi/v5/middleware"
    "fmt"
)
func main() {
    r := chi.NewRouter()

    // Add some middleware
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)

    // Define some routes
    r.Get("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Welcome to the home page!"))
    })

    r.Get("/hello/{name}", func(w http.ResponseWriter, r *http.Request) {
        name := chi.URLParam(r, "name")
        w.Write([]byte(fmt.Sprintf("Hello, %s!", name)))
    })
    http.ListenAndServe(":8080", r)
}

In this example, we create a new Chi router, add some middleware, and define a couple of routes. The middleware provided by Chi includes logging and panic recovery.

Middleware

Middleware in Chi is used to wrap HTTP handlers. Chi includes several built-in middleware functions that you can use to enhance your application’s functionality. Here are a few examples:

middleware.Logger: Logs the start and end of each request.
middleware.Recoverer: Recovers from panics and returns a 500 Internal Server Error.
You can also write custom middleware. Here’s a simple example of a middleware that adds a custom header to each response:

func CustomHeader(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("X-Custom-Header", "MyValue")
        next.ServeHTTP(w, r)
    })
}
func main() {
    r := chi.NewRouter()
    r.Use(middleware.Logger)
    r.Use(CustomHeader)

    r.Get("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Welcome to the home page!"))
    })

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

Routing with Chi
Chi provides a powerful and flexible way to define routes. You can use the standard HTTP methods (GET, POST, PUT, DELETE, etc.) and define URL parameters using {}.

URL Parameters
You can extract URL parameters using chi.URLParam.

r.Get("/user/{userID}", func(w http.ResponseWriter, r *http.Request) {
    userID := chi.URLParam(r, "userID")
    w.Write([]byte(fmt.Sprintf("User ID: %s", userID)))
})
Subrouters and Grouping Routes
Chi allows you to create subrouters, making it easy to group related routes.

r.Route("/users", func(r chi.Router) {
    r.Get("/", listUsers)    // GET /users
    r.Post("/", createUser)  // POST /users

    r.Route("/{userID}", func(r chi.Router) {
        r.Get("/", getUser)       // GET /users/{userID}
        r.Put("/", updateUser)    // PUT /users/{userID}
        r.Delete("/", deleteUser) // DELETE /users/{userID}
    })
})

This helps in organizing your routes and applying middleware to specific groups of routes.

Handling Static Files
Serving static files with Chi is straightforward using the FileServer middleware.

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

    // Serve static files from the ./static directory
    r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))

    http.ListenAndServe(":8080", r)
}
Context and Request-Scoped Data
Chi integrates seamlessly with Go's context.Context, allowing you to pass request-scoped data down the request chain.
func main() {
    r := chi.NewRouter()

    r.Get("/context", func(w http.ResponseWriter, r *http.Request) {
        ctx := context.WithValue(r.Context(), "user", "John Doe")
        nextHandler(w, r.WithContext(ctx))
    })

    r.Get("/next", func(w http.ResponseWriter, r *http.Request) {
        user := r.Context().Value("user").(string)
        w.Write([]byte(fmt.Sprintf("User: %s", user)))
    })

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

func nextHandler(w http.ResponseWriter, r *http.Request) {
    user := r.Context().Value("user").(string)
    w.Write([]byte(fmt.Sprintf("User: %s", user)))
}

In this example, we add a value to the context in one handler and retrieve it in another.

Conclusion

Chi is a powerful and flexible router for building web applications in Go. Its lightweight design and composable middleware stack make it an excellent choice for developing both small and large web services. Whether you’re building a simple API or a complex web application, Chi provides the tools you need to get the job done efficiently.

Support On Demand!

Golang