Go

Guia Completo de Chi Router em Go: Roteamento Idiomático e Middlewares Componíveis

16 min de leitura

Guia Completo de Chi Router em Go: Roteamento Idiomático e Middlewares Componíveis

O que é Chi e Por que Ele Importa 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 , o que significa que qualquer código Chi é compatível com o ecossistema padrão da biblioteca do Go. 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. Conceitos Fundamentais do Roteamento Estrutura Básica de uma Aplicação Chi Uma

<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 &quot;dialeto&quot; 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 (

&quot;fmt&quot;

&quot;net/http&quot;

&quot;github.com/go-chi/chi/v5&quot;

)

func main() {

r := chi.NewRouter()

// Registrar uma rota simples GET

r.Get(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {

w.WriteHeader(http.StatusOK)

fmt.Fprintf(w, &quot;Olá, mundo!&quot;)

})

http.ListenAndServe(&quot;:3000&quot;, 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 (

&quot;fmt&quot;

&quot;net/http&quot;

&quot;github.com/go-chi/chi/v5&quot;

)

func main() {

r := chi.NewRouter()

// Rota com parâmetro dinâmico

r.Get(&quot;/users/{userID}&quot;, func(w http.ResponseWriter, r *http.Request) {

userID := chi.URLParam(r, &quot;userID&quot;)

w.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)

fmt.Fprintf(w, {&quot;user_id&quot;: &quot;%s&quot;}, userID)

})

// Rota com múltiplos parâmetros

r.Get(&quot;/posts/{postID}/comments/{commentID}&quot;, func(w http.ResponseWriter, r *http.Request) {

postID := chi.URLParam(r, &quot;postID&quot;)

commentID := chi.URLParam(r, &quot;commentID&quot;)

fmt.Fprintf(w, &quot;Post %s, Comentário %s&quot;, postID, commentID)

})

http.ListenAndServe(&quot;:3000&quot;, 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 (

&quot;fmt&quot;

&quot;net/http&quot;

&quot;github.com/go-chi/chi/v5&quot;

)

func main() {

r := chi.NewRouter()

// Grupo de rotas para API v1

r.Route(&quot;/api/v1&quot;, func(r chi.Router) {

r.Get(&quot;/status&quot;, func(w http.ResponseWriter, r *http.Request) {

fmt.Fprintf(w, &quot;API v1 está ativa&quot;)

})

// Subrotas para usuários

r.Route(&quot;/users&quot;, func(r chi.Router) {

r.Get(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {

fmt.Fprintf(w, &quot;Lista de usuários&quot;)

})

r.Post(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {

fmt.Fprintf(w, &quot;Usuário criado&quot;)

})

r.Get(&quot;/{userID}&quot;, func(w http.ResponseWriter, r *http.Request) {

userID := chi.URLParam(r, &quot;userID&quot;)

fmt.Fprintf(w, &quot;Usuário %s&quot;, userID)

})

})

})

http.ListenAndServe(&quot;:3000&quot;, 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ê &quot;envolva&quot; 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 (

&quot;fmt&quot;

&quot;log&quot;

&quot;net/http&quot;

&quot;time&quot;

&quot;github.com/go-chi/chi/v5&quot;

)

// 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(&quot;Iniciando %s %s&quot;, r.Method, r.RequestURI)

next.ServeHTTP(w, r)

log.Printf(&quot;Concluído em %v&quot;, 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(&quot;Authorization&quot;)

if token == &quot;&quot; {

http.Error(w, &quot;Token ausente&quot;, 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(&quot;/public&quot;, func(w http.ResponseWriter, r *http.Request) {

fmt.Fprintf(w, &quot;Rota pública&quot;)

})

// Grupo protegido com middleware específico

r.Route(&quot;/protected&quot;, func(r chi.Router) {

r.Use(AuthMiddleware)

r.Get(&quot;/data&quot;, func(w http.ResponseWriter, r *http.Request) {

fmt.Fprintf(w, &quot;Dados privados&quot;)

})

})

http.ListenAndServe(&quot;:3000&quot;, 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 (

&quot;context&quot;

&quot;fmt&quot;

&quot;net/http&quot;

&quot;github.com/go-chi/chi/v5&quot;

)

// Tipo para armazenar valores no contexto

type ctxKey string

const userIDKey ctxKey = &quot;userID&quot;

// 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(&quot;X-User-ID&quot;)

if userID == &quot;&quot; {

http.Error(w, &quot;X-User-ID obrigatório&quot;, 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(&quot;/api&quot;, func(r chi.Router) {

r.Use(UserExtractorMiddleware)

r.Get(&quot;/profile&quot;, func(w http.ResponseWriter, r *http.Request) {

// Recuperar o userID do contexto

userID := r.Context().Value(userIDKey).(string)

fmt.Fprintf(w, &quot;Perfil do usuário: %s&quot;, userID)

})

})

http.ListenAndServe(&quot;:3000&quot;, 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 (

&quot;fmt&quot;

&quot;net/http&quot;

&quot;github.com/go-chi/chi/v5&quot;

&quot;github.com/go-chi/chi/v5/middleware&quot;

)

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(&quot;/test&quot;, func(w http.ResponseWriter, r *http.Request) {

// RequestID está disponível via middleware.GetReqID(r.Context())

reqID := middleware.GetReqID(r.Context())

fmt.Fprintf(w, &quot;Request ID: %s&quot;, reqID)

})

http.ListenAndServe(&quot;:3000&quot;, 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 (

&quot;encoding/json&quot;

&quot;fmt&quot;

&quot;net/http&quot;

&quot;github.com/go-chi/chi/v5&quot;

&quot;github.com/go-chi/chi/v5/middleware&quot;

)

type User struct {

ID string json:&quot;id&quot;

Name string json:&quot;name&quot;

Email string json:&quot;email&quot;

}

// Simulação de banco de dados

var users = map[string]User{

&quot;1&quot;: {ID: &quot;1&quot;, Name: &quot;Alice&quot;, Email: &quot;alice@example.com&quot;},

&quot;2&quot;: {ID: &quot;2&quot;, Name: &quot;Bob&quot;, Email: &quot;bob@example.com&quot;},

}

func GetUsers(w http.ResponseWriter, r *http.Request) {

w.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)

json.NewEncoder(w).Encode(users)

}

func CreateUser(w http.ResponseWriter, r *http.Request) {

var user User

json.NewDecoder(r.Body).Decode(&amp;user)

users[user.ID] = user

w.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)

w.WriteHeader(http.StatusCreated)

json.NewEncoder(w).Encode(user)

}

func GetUser(w http.ResponseWriter, r *http.Request) {

userID := chi.URLParam(r, &quot;userID&quot;)

user, exists := users[userID]

if !exists {

http.Error(w, &quot;Usuário não encontrado&quot;, http.StatusNotFound)

return

}

w.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)

json.NewEncoder(w).Encode(user)

}

func UpdateUser(w http.ResponseWriter, r *http.Request) {

userID := chi.URLParam(r, &quot;userID&quot;)

var user User

json.NewDecoder(r.Body).Decode(&amp;user)

user.ID = userID

users[userID] = user

w.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)

json.NewEncoder(w).Encode(user)

}

func DeleteUser(w http.ResponseWriter, r *http.Request) {

userID := chi.URLParam(r, &quot;userID&quot;)

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(&quot;/health&quot;, func(w http.ResponseWriter, r *http.Request) {

fmt.Fprintf(w, &quot;OK&quot;)

})

// API v1

r.Route(&quot;/api/v1&quot;, func(r chi.Router) {

r.Route(&quot;/users&quot;, func(r chi.Router) {

r.Get(&quot;/&quot;, GetUsers)

r.Post(&quot;/&quot;, CreateUser)

r.Route(&quot;/{userID}&quot;, func(r chi.Router) {

r.Get(&quot;/&quot;, GetUser)

r.Put(&quot;/&quot;, UpdateUser)

r.Delete(&quot;/&quot;, DeleteUser)

})

})

})

http.ListenAndServe(&quot;:3000&quot;, 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 (

&quot;context&quot;

&quot;database/sql&quot;

&quot;fmt&quot;

&quot;net/http&quot;

&quot;github.com/go-chi/chi/v5&quot;

)

// 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(&quot;Authorization&quot;)

if !authService.ValidateToken(token) {

http.Error(w, &quot;Token inválido&quot;, 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(), &quot;db&quot;, db)

next.ServeHTTP(w, r.WithContext(ctx))

})

}

}

func main() {

// Simular inicialização de serviços

authService := &amp;AuthService{apiKey: &quot;secret123&quot;}

// 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(&quot;/protected&quot;, func(w http.ResponseWriter, r *http.Request) {

fmt.Fprintf(w, &quot;Recurso protegido acessado com sucesso&quot;)

})

http.ListenAndServe(&quot;:3000&quot;, 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 &quot;tudo incluído&quot; 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>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Go

Select em Go: Multiplexando Channels e Timeouts: Do Básico ao Avançado
Select em Go: Multiplexando Channels e Timeouts: Do Básico ao Avançado

Introdução ao Select em Go O é uma das construções mais poderosas da linguage...

O que Todo Dev Deve Saber sobre Variáveis, Constantes, Tipos Primitivos e Inferência de Tipos em Go
O que Todo Dev Deve Saber sobre Variáveis, Constantes, Tipos Primitivos e Inferência de Tipos em Go

Variáveis em Go: Declaração, Atribuição e Escopo Uma variável é um local na m...

Introdução ao Go: Filosofia, Instalação, Toolchain e Primeiro Programa na Prática
Introdução ao Go: Filosofia, Instalação, Toolchain e Primeiro Programa na Prática

Por que Go? A Filosofia por Trás da Linguagem Go (ou Golang) foi criada em 20...