Golang - Chi - Cheatsheet

Just a guy who loves to write code and watch anime.
Search for a command to run...

Just a guy who loves to write code and watch anime.
No comments yet. Be the first to comment.
Qualities that make outstanding builders.

Bipedal vs quadrupedal: how many legs This is about the number of legs the creature walks on. Bipedal. Two legs. Humans, ostriches, T-rex, kangaroos, most fantasy humanoids. Quadrupedal. Four legs. Do

Intro You hear "lerp" and "smoothstep" everywhere in game dev. They sound like math jargon. They're not. Both are small tools that do the same job: smoothly move from one value to another. The problem

What is Kinematics Kinematics is the math field. It's the study of how things move without worrying about forces (which would be dynamics). FK and IK are the two branches: forward kinematics and inver

Intro Textures are usually the biggest cost in a 3D scene. Memory, bandwidth, and load time all get eaten by them. Resizing them is the obvious lever. There's more. This post is about the less obvious

go get github.com/go-chi/chi/v5
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!"))
}
r.Get("/users", getUsers)
r.Post("/users", createUser)
r.Put("/users/{id}", updateUser)
r.Delete("/users/{id}", deleteUser)
r.Patch("/users/{id}", patchUser)
// 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)
// 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)
// 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)
})
}
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
// 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)
}
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)
}
// 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"))
})
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())
}
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)
})
})
}
// 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)