Golang - Chi - Cheatsheet

Just a guy who loves to write code and watch anime.
Installation
go get github.com/go-chi/chi/v5
Basic Setup
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Get("/", homeHandler)
http.ListenAndServe(":8080", r)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!"))
}
HTTP Methods
r.Get("/users", getUsers)
r.Post("/users", createUser)
r.Put("/users/{id}", updateUser)
r.Delete("/users/{id}", deleteUser)
r.Patch("/users/{id}", patchUser)
URL Parameters
// Route: /users/{id}
func getUser(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "id")
// userID = "123" for URL: /users/123
}
// Multiple params: /articles/{date}-{slug}
date := chi.URLParam(r, "date")
slug := chi.URLParam(r, "slug")
// Wildcards: /files/*
filepath := chi.URLParam(r, "*")
// Regex: /posts/{id:^[0-9]+$}
r.Get("/posts/{id:^[0-9]+$}", getPost)
Route Groups & Sub-Routers
// Method 1: Route groups
r.Route("/api/v1", func(r chi.Router) {
r.Get("/users", getUsers) // GET /api/v1/users
r.Post("/users", createUser) // POST /api/v1/users
})
// Method 2: Mounting
apiRouter := chi.NewRouter()
apiRouter.Get("/users", getUsers)
r.Mount("/api/v1", apiRouter)
Middleware
// Global middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(60 * time.Second))
// Group-specific middleware
r.Group(func(r chi.Router) {
r.Use(AuthMiddleware)
r.Get("/profile", getProfile)
})
// Custom middleware
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "Unauthorized", 401)
return
}
next.ServeHTTP(w, r)
})
}
Common Middleware
import "github.com/go-chi/cors"
import "github.com/go-chi/httprate"
// CORS
r.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
}))
// Rate limiting
r.Use(httprate.LimitByIP(100, 1*time.Minute))
// Built-in middleware
r.Use(middleware.Compress(5)) // Gzip compression
r.Use(middleware.RealIP) // Real IP behind proxy
r.Use(middleware.Throttle(50)) // Max 50 concurrent requests
r.Use(middleware.NoCache) // No cache headers
Context Usage
// Setting context in middleware
func UserMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := getUserFromToken(r)
ctx := context.WithValue(r.Context(), "user", user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// Getting from context in handler
func getProfile(w http.ResponseWriter, r *http.Request) {
user := r.Context().Value("user").(*User)
json.NewEncoder(w).Encode(user)
}
JSON Handling
func createUser(w http.ResponseWriter, r *http.Request) {
var user User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, "Invalid JSON", 400)
return
}
// Process user...
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(201)
json.NewEncoder(w).Encode(user)
}
Error Handling
// Custom 404/405 handlers
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(404)
w.Write([]byte("Route not found"))
})
r.MethodNotAllowed(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(405)
w.Write([]byte("Method not allowed"))
})
Testing
func TestHandler(t *testing.T) {
r := chi.NewRouter()
r.Get("/test", testHandler)
req := httptest.NewRequest("GET", "/test", nil)
rr := httptest.NewRecorder()
r.ServeHTTP(rr, req)
assert.Equal(t, 200, rr.Code)
assert.Equal(t, "expected", rr.Body.String())
}
Real-World Structure
type Server struct {
Router *chi.Mux
DB *sql.DB
}
func NewServer(db *sql.DB) *Server {
s := &Server{DB: db}
s.Router = chi.NewRouter()
s.setupMiddleware()
s.setupRoutes()
return s
}
func (s *Server) setupMiddleware() {
s.Router.Use(middleware.Logger)
s.Router.Use(middleware.Recoverer)
s.Router.Use(cors.Handler(corsOptions))
}
func (s *Server) setupRoutes() {
// Public routes
s.Router.Group(func(r chi.Router) {
r.Get("/", s.home)
r.Post("/login", s.login)
})
// Protected routes
s.Router.Group(func(r chi.Router) {
r.Use(s.authMiddleware)
r.Route("/api", func(r chi.Router) {
r.Get("/users", s.getUsers)
r.Post("/users", s.createUser)
})
})
}
Common Patterns
// Health check
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte("OK"))
})
// Static files
workDir, _ := os.Getwd()
filesDir := http.Dir(filepath.Join(workDir, "static"))
FileServer(r, "/static", filesDir)
func FileServer(r chi.Router, path string, root http.FileSystem) {
fs := http.StripPrefix(path, http.FileServer(root))
r.Get(path+"/*", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fs.ServeHTTP(w, r)
}))
}
// Graceful shutdown
server := &http.Server{Addr: ":8080", Handler: r}
go server.ListenAndServe()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
server.Shutdown(ctx)






