DEV Community

Alex Spinov
Alex Spinov

Posted on

Go Chi Has a Free API — Lightweight Router for Production Go Services

Chi is a lightweight, composable router for Go HTTP services. It's 100% compatible with net/http, has zero dependencies, and powers some of the largest Go services in production.

Why Chi?

  • Zero dependencies — only uses stdlib
  • net/http compatible — all http.Handler middleware works
  • URL parameters/users/{id} with type-safe extraction
  • Middleware stack — composable, chainable middleware

Quick Start

go get github.com/go-chi/chi/v5
Enter fullscreen mode Exit fullscreen mode
package main

import (
    "encoding/json"
    "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("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, Chi!"))
    })

    http.ListenAndServe(":3000", r)
}
Enter fullscreen mode Exit fullscreen mode

RESTful Routes

type User struct {
    ID    string `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

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

    r.Route("/api/users", func(r chi.Router) {
        r.Get("/", listUsers)
        r.Post("/", createUser)
        r.Route("/{userID}", func(r chi.Router) {
            r.Get("/", getUser)
            r.Put("/", updateUser)
            r.Delete("/", deleteUser)
        })
    })

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

func getUser(w http.ResponseWriter, r *http.Request) {
    userID := chi.URLParam(r, "userID")
    user := User{ID: userID, Name: "Alice", Email: "alice@example.com"}
    json.NewEncoder(w).Encode(user)
}

func createUser(w http.ResponseWriter, r *http.Request) {
    var user User
    json.NewDecoder(r.Body).Decode(&user)
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(user)
}
Enter fullscreen mode Exit fullscreen mode

Middleware

import "github.com/go-chi/chi/v5/middleware"

r := chi.NewRouter()

// Built-in middleware
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(60 * time.Second))
r.Use(middleware.Compress(5))

// 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)
    })
}

// Apply to specific routes
r.Group(func(r chi.Router) {
    r.Use(authMiddleware)
    r.Get("/admin", adminHandler)
    r.Get("/dashboard", dashboardHandler)
})
Enter fullscreen mode Exit fullscreen mode

Subrouters

func apiRouter() chi.Router {
    r := chi.NewRouter()
    r.Use(apiAuth)
    r.Get("/health", healthCheck)
    r.Mount("/users", userRouter())
    r.Mount("/posts", postRouter())
    return r
}

func main() {
    r := chi.NewRouter()
    r.Mount("/api/v1", apiRouter())
    http.ListenAndServe(":3000", r)
}
Enter fullscreen mode Exit fullscreen mode

Building Go microservices that need web data? Check out my Apify actors for ready-made scrapers, or email spinov001@gmail.com for custom Go solutions.

Chi, Gin, or Fiber — which Go router do you use? Share below!

Top comments (0)