<h2>O que é Chi e Por que Ele Importa</h2>
<p>Chi é um roteador HTTP leve e expressivo para Go que se destaca por sua arquitetura idiomática e suporte nativo a middlewares compostos. Diferente de outros frameworks web em Go que tentam replicar patterns de linguagens dinâmicas, Chi foi construído desde o início respeitando os princípios de simplicidade e composição que caracterizam a linguagem. Ele implementa a interface <code>http.Handler</code>, o que significa que qualquer código Chi é compatível com o ecossistema padrão da biblioteca <code>net/http</code> do Go.</p>
<p>A proposta central do Chi é oferecer um roteador que não adicione overhead desnecessário, mas que permita escrever rotas de forma clara e com suporte primeiro para middlewares. Você não precisa aprender um "dialeto" especial ou magic frameworks — apenas Go puro, com uma camada de abstração bem pensada. Isso torna Chi particularmente atrativo para quem valoriza legibilidade, testabilidade e performance em aplicações web.</p>
<h2>Conceitos Fundamentais do Roteamento</h2>
<h3>Estrutura Básica de uma Aplicação Chi</h3>
<p>Uma aplicação Chi começa com a criação de um roteador via <code>chi.NewRouter()</code>. Este objeto é responsável por registrar rotas e middlewares, e pode ser passado diretamente para <code>http.ListenAndServe()</code> porque implementa <code>http.Handler</code>.</p>
<pre><code class="language-go">package main
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
// Registrar uma rota simples GET
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Olá, mundo!")
})
http.ListenAndServe(":3000", r)
}</code></pre>
<p>Neste exemplo, estamos criando um roteador que escuta na porta 3000 e responde com uma mensagem na rota raiz. Note que a função handler segue a assinatura padrão <code>func(w http.ResponseWriter, r *http.Request)</code>, não há nada proprietário aqui.</p>
<h3>Parâmetros de Rota (Route Parameters)</h3>
<p>Chi permite capturar parâmetros dinâmicos na URL através da sintaxe <code>{nomeDoParâmetro}</code>. Esses valores são extraídos e podem ser acessados usando <code>chi.URLParam()</code>.</p>
<pre><code class="language-go">package main
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
// Rota com parâmetro dinâmico
r.Get("/users/{userID}", func(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "userID")
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, {"user_id": "%s"}, userID)
})
// Rota com múltiplos parâmetros
r.Get("/posts/{postID}/comments/{commentID}", func(w http.ResponseWriter, r *http.Request) {
postID := chi.URLParam(r, "postID")
commentID := chi.URLParam(r, "commentID")
fmt.Fprintf(w, "Post %s, Comentário %s", postID, commentID)
})
http.ListenAndServe(":3000", r)
}</code></pre>
<p>Chi não força tipagem de parâmetros — você recebe strings e decide como validar ou converter. Isso oferece controle fino, embora exija disciplina do desenvolvedor.</p>
<h3>Agrupamento de Rotas (Route Groups)</h3>
<p>Para aplicações que crescem, é essencial organizar rotas em grupos lógicos. Chi permite isso através do método <code>Group()</code>, que retorna um novo roteador aninhado sobre o qual você pode registrar subrotas e middlewares específicos.</p>
<pre><code class="language-go">package main
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
// Grupo de rotas para API v1
r.Route("/api/v1", func(r chi.Router) {
r.Get("/status", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "API v1 está ativa")
})
// Subrotas para usuários
r.Route("/users", func(r chi.Router) {
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Lista de usuários")
})
r.Post("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Usuário criado")
})
r.Get("/{userID}", func(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "userID")
fmt.Fprintf(w, "Usuário %s", userID)
})
})
})
http.ListenAndServe(":3000", r)
}</code></pre>
<p>Este padrão permite que você estruture sua API de forma hierárquica, refletindo a organização conceitual das suas funcionalidades. Cada grupo pode ter seus próprios middlewares, como veremos adiante.</p>
<h2>Middlewares: O Coração da Composição</h2>
<h3>Entendendo Middlewares em Go</h3>
<p>Um middleware em Go é simplesmente uma função que recebe um <code>http.Handler</code> e retorna outro <code>http.Handler</code>. Essa estrutura permite que você "envolva" handlers com lógica transversal como autenticação, logging, compressão, etc. Chi facilita o registro e composição desses middlewares.</p>
<pre><code class="language-go">package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/go-chi/chi/v5"
)
// Middleware para logar requisições
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
log.Printf("Iniciando %s %s", r.Method, r.RequestURI)
next.ServeHTTP(w, r)
log.Printf("Concluído em %v", time.Since(start))
})
}
// Middleware para verificar autorização (exemplo simplificado)
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, "Token ausente", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
func main() {
r := chi.NewRouter()
// Registrar middlewares globalmente (aplicados em todas as rotas)
r.Use(LoggingMiddleware)
r.Get("/public", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Rota pública")
})
// Grupo protegido com middleware específico
r.Route("/protected", func(r chi.Router) {
r.Use(AuthMiddleware)
r.Get("/data", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Dados privados")
})
})
http.ListenAndServe(":3000", r)
}</code></pre>
<p>Note como <code>Use()</code> registra um middleware, e quando você chama <code>Use()</code> dentro de um <code>Route()</code>, o middleware aplica-se apenas àquele grupo e suas subrotas. Isso é composição idiomática em Go.</p>
<h3>Middlewares Compostos e Contexto</h3>
<p>Chi integra-se perfeitamente com <code>context.Context</code>, permitindo que middlewares transmitam dados para handlers através do contexto da requisição. Esse padrão evita dependências globais e torna o fluxo de dados explícito.</p>
<pre><code class="language-go">package main
import (
"context"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
// Tipo para armazenar valores no contexto
type ctxKey string
const userIDKey ctxKey = "userID"
// Middleware que extrai e valida um ID de usuário
func UserExtractorMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userID := r.Header.Get("X-User-ID")
if userID == "" {
http.Error(w, "X-User-ID obrigatório", http.StatusBadRequest)
return
}
// Armazenar o userID no contexto
ctx := context.WithValue(r.Context(), userIDKey, userID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func main() {
r := chi.NewRouter()
r.Route("/api", func(r chi.Router) {
r.Use(UserExtractorMiddleware)
r.Get("/profile", func(w http.ResponseWriter, r *http.Request) {
// Recuperar o userID do contexto
userID := r.Context().Value(userIDKey).(string)
fmt.Fprintf(w, "Perfil do usuário: %s", userID)
})
})
http.ListenAndServe(":3000", r)
}</code></pre>
<p>Este padrão é fundamental em Go moderno. O contexto flui através de toda a cadeia de middleware e handler, permitindo que lógica compartilhada seja injetada sem acoplamento.</p>
<h3>Middlewares de Terceiros e Padrões Comuns</h3>
<p>Chi é compatível com middlewares populares do ecossistema Go. A comunidade mantém um conjunto de middlewares úteis e testados que podem ser usados diretamente.</p>
<pre><code class="language-go">package main
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
r := chi.NewRouter()
// Middlewares do próprio Chi para tarefas comuns
r.Use(middleware.RequestID) // Gera ID único para cada requisição
r.Use(middleware.RealIP) // Extrai IP real (útil com proxies)
r.Use(middleware.Logger) // Logging estruturado
r.Use(middleware.Recoverer) // Recupera-se de panics
r.Get("/test", func(w http.ResponseWriter, r *http.Request) {
// RequestID está disponível via middleware.GetReqID(r.Context())
reqID := middleware.GetReqID(r.Context())
fmt.Fprintf(w, "Request ID: %s", reqID)
})
http.ListenAndServe(":3000", r)
}</code></pre>
<p>Os middlewares embutidos no Chi (<code>github.com/go-chi/chi/v5/middleware</code>) cobrem cenários comuns como logging, recuperação de erros e extração de metadados de requisição. Você pode combinar esses com seus próprios middlewares.</p>
<h2>Padrões Avançados e Organização</h2>
<h3>RESTful API Completa com Chi</h3>
<p>Chi brilha quando você precisa construir APIs RESTful bem estruturadas. Combinando agrupamento de rotas com middlewares, é possível criar aplicações limpas e manuteníveis.</p>
<pre><code class="language-go">package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
type User struct {
ID string json:"id"
Name string json:"name"
Email string json:"email"
}
// Simulação de banco de dados
var users = map[string]User{
"1": {ID: "1", Name: "Alice", Email: "alice@example.com"},
"2": {ID: "2", Name: "Bob", Email: "bob@example.com"},
}
func GetUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
}
func CreateUser(w http.ResponseWriter, r *http.Request) {
var user User
json.NewDecoder(r.Body).Decode(&user)
users[user.ID] = user
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
}
func GetUser(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "userID")
user, exists := users[userID]
if !exists {
http.Error(w, "Usuário não encontrado", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
func UpdateUser(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "userID")
var user User
json.NewDecoder(r.Body).Decode(&user)
user.ID = userID
users[userID] = user
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
func DeleteUser(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "userID")
delete(users, userID)
w.WriteHeader(http.StatusNoContent)
}
func main() {
r := chi.NewRouter()
// Middlewares globais
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
// Rotas de healthcheck
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "OK")
})
// API v1
r.Route("/api/v1", func(r chi.Router) {
r.Route("/users", func(r chi.Router) {
r.Get("/", GetUsers)
r.Post("/", CreateUser)
r.Route("/{userID}", func(r chi.Router) {
r.Get("/", GetUser)
r.Put("/", UpdateUser)
r.Delete("/", DeleteUser)
})
})
})
http.ListenAndServe(":3000", r)
}</code></pre>
<p>Neste exemplo, a estrutura RESTful é clara: usuários podem ser listados, criados, recuperados individualmente, atualizados e deletados. Cada operação tem seu próprio handler, e as rotas refletem perfeitamente a semântica HTTP.</p>
<h3>Middlewares Reutilizáveis com Dependências</h3>
<p>Em aplicações reais, middlewares frequentemente precisam acessar serviços ou configurações. Chi permite criar middlewares que aceitam parâmetros e retornam o middleware propriamente dito.</p>
<pre><code class="language-go">package main
import (
"context"
"database/sql"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
// Simular um serviço de autenticação
type AuthService struct {
apiKey string
}
func (as *AuthService) ValidateToken(token string) bool {
return token == as.apiKey
}
// Factory para criar middleware com injeção de dependência
func AuthMiddlewareFactory(authService *AuthService) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if !authService.ValidateToken(token) {
http.Error(w, "Token inválido", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
}
// Middleware que injeta um banco de dados no contexto
func DatabaseMiddleware(db *sql.DB) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), "db", db)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
func main() {
// Simular inicialização de serviços
authService := &AuthService{apiKey: "secret123"}
// db := connectToDatabase() // em uma aplicação real
r := chi.NewRouter()
// Registrar middlewares factory com dependências
r.Use(AuthMiddlewareFactory(authService))
// r.Use(DatabaseMiddleware(db))
r.Get("/protected", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Recurso protegido acessado com sucesso")
})
http.ListenAndServe(":3000", r)
}</code></pre>
<p>Este padrão é essencial em aplicações que crescem: a injeção de dependência garante que seus middlewares permaneçam testáveis e desacoplados da implementação específica dos serviços.</p>
<h2>Conclusão</h2>
<p>Aprender Chi significa aprender a escrever código Go idiomático para construir APIs web. Os três pontos principais que você deve levar desta leitura são: <strong>primeiro</strong>, roteamento em Chi é simples e composável porque respeita a interface padrão <code>http.Handler</code>, mantendo compatibilidade com todo o ecossistema Go; <strong>segundo</strong>, middlewares são funções de primeira classe que permitem cruzar responsabilidades (autenticação, logging, validação) de forma elegante e reutilizável através do contexto; <strong>terceiro</strong>, a organização em grupos de rotas (<code>Route()</code>) aliada ao suporte a middlewares por grupo permite que você cresça de uma aplicação simples para uma API complexa sem refatorações radicais.</p>
<p>Chi não é um framework monolítico — é uma ferramenta focada que você compõe com outras bibliotecas do Go. Isso exige disciplina, mas oferece flexibilidade e clareza que frameworks "tudo incluído" dificilmente conseguem.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://github.com/go-chi/chi" target="_blank" rel="noopener noreferrer">Documentação Oficial Chi</a></li>
<li><a href="https://pkg.go.dev/net/http" target="_blank" rel="noopener noreferrer">Go net/http Documentation</a></li>
<li><a href="https://github.com/go-chi/chi/tree/master/middleware" target="_blank" rel="noopener noreferrer">Chi Middleware Collection</a></li>
<li><a href="https://golang.org/doc/articles/wiki/" target="_blank" rel="noopener noreferrer">Building Web Applications with Go</a></li>
<li><a href="https://pkg.go.dev/context" target="_blank" rel="noopener noreferrer">Context Package Documentation</a></li>
</ul>
<p><!-- FIM --></p>