Weekly Library | Chi: An Elegant HTTP Router Library for Go

Weekly Library | Chi: 🚦 An Elegant HTTP Router Library for Go

When building web services and APIs, an excellent routing library can greatly enhance development efficiency. Chi, with its lightweight design and powerful features, has become a highly regarded HTTP routing solution in the Go language ecosystem.

πŸ’‘ Understanding Chi

Chi is a lightweight routing library built on the standard library <span>net/http</span>, created by Peter Kieltyka, the technical lead at Pressly. It adheres to Go’s design philosophy of simplicity and efficiency, having garnered over 14,000 stars on GitHub, making it one of the most popular HTTP routing libraries in the Go ecosystem.

✨ Core Features

βœ… Fully compatible with the standard library: Seamless integration with <span>net/http</span>, no need to learn new conceptsβœ… No external dependencies: Does not rely on any packages other than the Go standard libraryβœ… Highly expressive routing: Supports route groups, path parameters, and wildcardsβœ… Elegant middleware system: Global, group, and single-route level middlewareβœ… Context control: Pass request values through the standard <span>context.Context</span>βœ… Modular design: Components can be used individually or flexibly combinedβœ… High performance: Performance close to native <span>net/http</span>‘s performance

πŸš€ Quick Start

πŸ“¦ Installing Chi

// Install Chi using Go modules
go get -u github.com/go-chi/chi/v5

πŸ”° Basic Example

package main

import (
    "net/http"
    "github.com/go-chi/chi/v5"
    "github.com/go-chi/chi/v5/middleware"
)

func main() {
    // Create a router
    r := chi.NewRouter()
    
    // Global middleware
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)
    
    // Basic route
    r.Get("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Welcome to the Chi example!"))
    })
    
    // Route with parameters
    r.Get("/hello/{name}", func(w http.ResponseWriter, r *http.Request) {
        name := chi.URLParam(r, "name")
        w.Write([]byte("Hello, " + name + "!"))
    })
    
    // Start the server
    http.ListenAndServe(":3000", r)
}

🌐 Routing System Explained

🧩 Routing Methods

Chi provides a simple API for all HTTP methods:

// Handler functions for HTTP methods
r.Get("/users", listUsers)          // GET request
r.Post("/users", createUser)        // POST request
r.Put("/users/{id}", updateUser)    // PUT request
r.Delete("/users/{id}", deleteUser) // DELETE request
r.Patch("/users/{id}", patchUser)   // PATCH request

πŸ“ URL Parameters and Pattern Matching

Chi supports flexible route parameters and pattern matching:

// Simple parameters
r.Get("/users/{id}", getUser)

// Regex constrained parameters
r.Get("/articles/{year:[0-9]{4}}/{month:[0-9]{2}}", getArticlesByMonth)

// Wildcard matching
r.Get("/files/{filepath:*}", serveFiles)

πŸ—‚οΈ Route Organization and Nesting

Chi’s route grouping feature makes organizing large APIs clear:

// API versioning and resource grouping
r.Route("/api", func(r chi.Router) {
    // V1 API
    r.Route("/v1", func(r chi.Router) {
        // User resources
        r.Route("/users", func(r chi.Router) {
            r.Get("/", listUsers)        // GET /api/v1/users
            r.Post("/", createUser)      // POST /api/v1/users
            
            // Single user operations
            r.Route("/{id}", func(r chi.Router) {
                r.Get("/", getUser)       // GET /api/v1/users/123
                r.Put("/", updateUser)    // PUT /api/v1/users/123
                r.Delete("/", deleteUser) // DELETE /api/v1/users/123
            })
        })
    })
})

πŸ”Œ Route Mounting

The modular design allows you to mount independent routers onto the main router:

// User router
func userRouter() http.Handler {
    r := chi.NewRouter()
    r.Get("/", listUsers)
    r.Post("/", createUser)
    // ...
    return r
}

// Product router
func productRouter() http.Handler {
    r := chi.NewRouter()
    r.Get("/", listProducts)
    // ...
    return r
}

// Main router
r := chi.NewRouter()
r.Mount("/users", userRouter())     // Mounted at /users path
r.Mount("/products", productRouter()) // Mounted at /products path

πŸ”„ Middleware System

Middleware is a powerful feature of Chi, allowing you to execute code at various stages of request processing.

πŸ“‹ Built-in Middleware

Chi provides various useful middleware:

r := chi.NewRouter()

// Request logging
r.Use(middleware.Logger)

// Recover from panic
r.Use(middleware.Recoverer)

// Request ID tracking
r.Use(middleware.RequestID)

// Request timeout control
r.Use(middleware.Timeout(30 * time.Second))

// Response compression
r.Use(middleware.Compress(5))

πŸ› οΈ Custom Middleware

Creating your own middleware is very simple:

// Authentication middleware example
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        
        if !isValidToken(token) {
            http.Error(w, "Unauthorized access", http.StatusUnauthorized)
            return
        }
        
        // Continue processing the request
        next.ServeHTTP(w, r)
    })
}

// Using custom middleware
r.Use(AuthMiddleware)

🎯 Middleware Application Scope

Middleware can be applied at different levels:

// Global middleware
r.Use(middleware.Logger)

// Route group middleware
r.Route("/admin", func(r chi.Router) {
    r.Use(adminOnly) // Only effective for /admin path
    // ...
})

// Single route middleware
r.With(cacheControl).Get("/public", servePublicContent)

πŸ“Š RESTful API Practical Example

Here is a complete RESTful API example:

func main() {
    r := chi.NewRouter()
    
    // Basic middleware
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)
    
    // API routes
    r.Route("/api/users", func(r chi.Router) {
        r.Get("/", listUsers)       // Get all users
        r.Post("/", createUser)     // Create user
        
        r.Route("/{id}", func(r chi.Router) {
            r.Get("/", getUser)        // Get single user
            r.Put("/", updateUser)     // Update user
            r.Delete("/", deleteUser)  // Delete user
        })
    })
    
    http.ListenAndServe(":3000", r)
}

// Handler function example
func listUsers(w http.ResponseWriter, r *http.Request) {
    users := []User{
        {ID: "1", Name: "Zhang San"},
        {ID: "2", Name: "Li Si"},
    }
    respondJSON(w, http.StatusOK, users)
}

func getUser(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")
    // Fetch user from database
    user := User{ID: id, Name: "Zhang San"}
    respondJSON(w, http.StatusOK, user)
}

// Helper function
func respondJSON(w http.ResponseWriter, status int, data interface{}) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(data)
}

πŸ”’ Security Best Practices

Key points for building secure applications with Chi:

Security Headers Middleware

func securityHeaders(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Prevent clickjacking
        w.Header().Set("X-Frame-Options", "DENY")
        // Prevent MIME sniffing
        w.Header().Set("X-Content-Type-Options", "nosniff")
        // XSS protection
        w.Header().Set("X-XSS-Protection", "1; mode=block")
        
        next.ServeHTTP(w, r)
    })
}

// Apply security headers
r.Use(securityHeaders)

πŸ§ͺ Testing Chi Applications

Chi is fully compatible with Go’s testing tools, making it easy to write tests:

func TestGetUser(t *testing.T) {
    // Create request
    req := httptest.NewRequest("GET", "/users/123", nil)
    w := httptest.NewRecorder()
    
    // Set up routes
    r := chi.NewRouter()
    r.Get("/users/{id}", getUser)
    
    // Handle request
    r.ServeHTTP(w, req)
    
    // Validate result
    if w.Code != 200 {
        t.Errorf("Expected status code 200, got %d", w.Code)
    }
}

πŸ“ˆ Routing Framework Comparison

Comparison of Chi with other popular Go HTTP routing frameworks:

Feature Chi Gin Standard Library Gorilla Mux
Compatibility with Standard Library ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Routing Features ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐
Performance ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐
Middleware Support ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐
Usability ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐
External Dependencies ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Community Support ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐

πŸ’‘ Summary

Chi provides an elegant and powerful HTTP routing solution by cleverly balancing simplicity and functionality. Its design philosophy aligns with Go, emphasizing practicality, performance, and code readability.

Scenarios Where Chi is Particularly Suitable

  • β€’ Building RESTful APIs
  • β€’ Microservices architecture
  • β€’ Performance-sensitive web services
  • β€’ Projects that require tight integration with the standard library

Whether you are developing simple APIs or complex web applications, Chi provides the right abstractions and tools to help you build high-quality Go web services more efficiently.

Learning Resources:

  • β€’ Chi GitHub Repository
  • β€’ Chi Official Documentation
  • β€’ Chi Example Code

πŸ€” Today’s Discussion: Which routing framework do you use in Go web development? What do you think are the pros and cons compared to Chi?

πŸ”₯ Join the Community: Reply with 【Join Group】 to exchange learning experiences with thousands of Go developers and share practical insights!

πŸ‘‰ Follow 【Gopher Tribe】 for continuous updates on Go language technical content, with quality updates every week.

Leave a Comment