RepoPilotOpen in app →

go-chi/chi

lightweight, idiomatic and composable router for building Go HTTP services

Healthy

Healthy across the board

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 3mo ago
  • 56+ active contributors
  • Distributed ownership (top contributor 21% of recent commits)
Show 3 more →
  • MIT licensed
  • CI configured
  • Tests present

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/go-chi/chi)](https://repopilot.app/r/go-chi/chi)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/go-chi/chi on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: go-chi/chi

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/go-chi/chi shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 3mo ago
  • 56+ active contributors
  • Distributed ownership (top contributor 21% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live go-chi/chi repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/go-chi/chi.

What it runs against: a local clone of go-chi/chi — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in go-chi/chi | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 109 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>go-chi/chi</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of go-chi/chi. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/go-chi/chi.git
#   cd chi
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of go-chi/chi and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "go-chi/chi(\\.git)?\\b" \\
  && ok "origin remote is go-chi/chi" \\
  || miss "origin remote is not go-chi/chi (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "mux.go" \\
  && ok "mux.go" \\
  || miss "missing critical file: mux.go"
test -f "chi.go" \\
  && ok "chi.go" \\
  || miss "missing critical file: chi.go"
test -f "tree.go" \\
  && ok "tree.go" \\
  || miss "missing critical file: tree.go"
test -f "context.go" \\
  && ok "context.go" \\
  || miss "missing critical file: context.go"
test -f "chain.go" \\
  && ok "chain.go" \\
  || miss "missing critical file: chain.go"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 109 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~79d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/go-chi/chi"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

chi is a lightweight HTTP router for Go that provides composable, modular routing for REST APIs using only the Go standard library and context package. It's built for writing maintainable large-scale REST services with middleware support, route grouping, and sub-router mounting in under 1000 lines of code. Single-package core: chi.go and chain.go form the router engine (~1000 LOC total). Companion optional subpackages under middleware/ for cross-cutting concerns. _examples/ directory contains 12+ runnable examples (hello-world, rest, graceful-shutdown, file-serving) demonstrating real usage patterns. No monorepo complexity—flat structure with examples as peer directories.

👥Who it's for

Go backend developers building REST APIs and microservices who need clean, idiomatic routing without external dependencies. DevOps engineers at scale (used by Pressly, Cloudflare, Heroku, 99Designs) and teams maintaining large codebases that need composable middleware and route management.

🌱Maturity & risk

Production-ready and actively maintained. The project is in use at major companies (Pressly, Cloudflare, Heroku), has established CI/CD workflows (.github/workflows/ci.yml), comprehensive examples in _examples/, and clear governance (CONTRIBUTING.md, SECURITY.md). Version 5 added go.mod support and the codebase is stable with regular maintenance.

Low risk overall—chi explicitly targets zero external dependencies (plain Go stdlib only), reducing supply chain concerns. Single-maintainer projects exist, but the public adoption list and long track record suggest sustainability. No obvious breaking changes in visible recent history, though v5 migration from v4 is noted in CHANGELOG.md.

Active areas of work

Visible from structure: the project maintains active CI (workflows/ci.yml), has comprehensive examples, and tracks changes in CHANGELOG.md. The v5 release introduced go.mod support. No PR backlog or open issue data visible in provided structure, but SECURITY.md and CONTRIBUTING.md indicate ongoing maintenance and clear contribution pathways.

🚀Get running

git clone https://github.com/go-chi/chi.git
cd chi
go mod download
cd _examples/hello-world
go run main.go
# Server runs on :3000

Daily commands:

cd _examples/hello-world && go run main.go
# Or use the Makefile:
make test
# For any example: cd _examples/{example-name} && go run main.go

🗺️Map of the codebase

  • mux.go — Core router implementation defining the Mux type and route matching logic—the heart of chi's request dispatching
  • chi.go — Public API entry point exposing NewRouter(), NewMux() and core router constructor functions every contributor must understand
  • tree.go — Radix tree implementation for efficient path pattern matching and parameter extraction—critical to routing performance
  • context.go — Request-scoped value management and context utilities—enables chi's handler chain pattern with request data passing
  • chain.go — Middleware chaining abstraction defining how handlers are composed and executed in sequence
  • middleware/middleware.go — Middleware package interface and utilities—entry point for understanding chi's optional middleware ecosystem
  • README.md — Design philosophy and idiomatic Go HTTP patterns chi follows—essential context for architectural decisions

🧩Components & responsibilities

  • Mux (router core) (net/http.Handler, context.Context, Radix tree) — Accept incoming HTTP requests, match against route tree, construct handler chain, dispatch to middleware and handlers
    • Failure mode: Route mismatch returns 404; handler panic crashes request (recoverer middleware mitigates)
  • Radix tree (path matching) (Trie data structure) — Efficiently match URL paths against pattern definitions and extract named parameters ({id}, {slug})
    • Failure mode: Ambiguous pattern definitions produce undefined behavior; missing radix node returns no match
  • Context (request scope) (context.Context, sync.RWMutex-protected map) — Store and retrieve route parameters, custom values, and middleware data associated with a single request
    • Failure mode: Key collisions silently overwrite values; non-serializable data in context may cause issues if logged

🛠️How to make changes

Add a new REST endpoint

  1. Define route in your mux using r.Get(), r.Post(), etc. syntax (mux.go)
  2. Write handler function with http.ResponseWriter and *http.Request signature (_examples/rest/main.go)
  3. Extract path parameters using chi.URLParam(r, "paramName") in handler (context.go)
  4. Apply middleware to specific routes via r.With() before defining subroutes (mux.go)

Add custom middleware

  1. Create middleware function matching func(next http.Handler) http.Handler pattern (middleware/middleware.go)
  2. Return wrapped handler that calls next.ServeHTTP(w, r) after processing (middleware/recoverer.go)
  3. Apply globally via r.Use() or to specific routes via r.With() (mux.go)
  4. Access route context via chi.RouteCtx(r.Context()) if needed for routing info (context.go)

Organize routes with subrouters and groups

  1. Create subrouter with r.Route("/prefix", func(r chi.Router) {...}) (mux.go)
  2. Apply authentication/middleware to group with r.Group(func(r chi.Router) {...}) (_examples/rest/main.go)
  3. Nest subrouters arbitrarily deep for resource hierarchies (e.g. /users/{id}/posts/{postId}) (_examples/todos-resource/main.go)
  4. Access nested parameters in handler via chi.URLParam(r, "id"), chi.URLParam(r, "postId") (context.go)

Add request/response logging and error recovery

  1. Import middleware package and apply r.Use(middleware.Logger()) globally (middleware/logger.go)
  2. Add r.Use(middleware.Recoverer()) to catch panics and return 500 (middleware/recoverer.go)
  3. Customize log output by passing custom *log.Logger to middleware.RequestLogger() (middleware/logger.go)
  4. Add request timeouts with r.Use(middleware.Timeout(time.Duration)) (middleware/timeout.go)

🔧Why these technologies

  • Go context package (Go 1.7+) — Enables request-scoped value passing and cancellation signals across handler chains without goroutine locals or global state
  • Radix tree for path matching — Provides O(k) lookup where k is path depth, enabling efficient routing for large route tables without regex compilation overhead
  • Middleware as http.Handler wrappers — Follows stdlib conventions (net/http), maximizes composability with third-party libraries, and eliminates framework lock-in
  • Composable subrouting with Group/Route — Enables clean separation of concerns and DRY route definitions as APIs grow, reducing maintenance burden

⚖️Trade-offs already made

  • Radix tree instead of regex-based routing

    • Why: Radix trees are faster (O(k) vs potential catastrophic backtracking) and deterministic, improving predictability at scale
    • Consequence: Path patterns are limited to parameter placeholders ({param}) rather than arbitrary regex; users must use custom logic for complex pattern matching
  • Minimal core (< 1000 LOC) with optional middleware subpackage

    • Why: Keeps framework lightweight, approachable, and maintainable while offering composable building blocks
    • Consequence: Users must explicitly import and apply middleware; no batteries-included defaults may surprise newcomers from opinionated frameworks
  • Request-scoped storage via context instead of request properties

    • Why: Idiomatic Go, enables cancellation propagation, and works correctly in goroutine pools without lifecycle management
    • Consequence: Requires discipline to avoid context key collisions; developers must avoid putting non-serializable/non-thread-safe data in context
  • No built-in templating, ORM, or database layer

    • Why: Keeps chi focused on HTTP routing; users choose own rendering/persistence libraries
    • Consequence: Requires more scaffolding for REST APIs than heavier frameworks; developers must integrate tools themselves

🚫Non-goals (don't propose these)

  • Does not provide built-in request validation or schema enforcement
  • Does not include template rendering or view layer (users integrate html/template or render library separately)
  • Does not manage database connections or ORM (users integrate GORM, sqlc, etc.)
  • Does not provide request/response marshaling (users integrate encoding/json or render library)
  • Does not enforce API versioning or content negotiation (middleware available but not enforced)
  • Not suitable for non-HTTP protocols (gRPC, WebSocket, etc. require separate frameworks)

🪤Traps & gotchas

No external config: chi has no config files or environment variable loading—all routing is programmatic in code. Context.Context required: handlers must accept *http.Request with populated context; some middleware patterns require understanding Go's context cancellation. Goroutine-safe routing: routes must be registered before starting the server; runtime route addition is not thread-safe. No built-in request validation: chi only does routing; you must manually validate request bodies, query params, and headers. Middleware order matters: Use() applies globally in order; With() applies to specific routes—confusion between these causes silent bugs.

🏗️Architecture

💡Concepts to learn

  • Middleware chaining (composition pattern) — chi.chain.go implements the core concept of request pre/post-processing; understanding Use() (global) vs With() (route-specific) is essential to building composable APIs
  • Context propagation through HTTP handlers — chi is built on Go 1.7's context package for request-scoped cancellation and value passing; chi.chi.go threads context through middleware—required to understand chi's design philosophy
  • Sub-router mounting and route grouping — chi allows nested routers (r.Route('/api', func(r chi.Router){})) for organizing large APIs; this is demonstrated in _examples/rest/main.go and is central to chi's 'composable' design
  • HTTP Handler interface (stdlib compatibility) — chi uses only net/http.Handler interface—all middleware must implement this; this stdlib-first design is chi's key competitive advantage
  • Path parameter extraction and URL pattern matching — chi supports wildcard and regex patterns in routes (e.g., /users/{id}, /files/*); chi.URLParam() retrieves matched values from context—core to REST API routing
  • Idempotent request processing — REST APIs using chi must handle middleware carefully to ensure handlers are idempotent; chi's context threading can mask state mutations if not careful
  • gorilla/mux — Older, more feature-rich router alternative; many projects migrate from gorilla/mux to chi for lighter weight and context-native design
  • labstack/echo — Full-stack web framework with routing; chi users choose chi specifically to avoid this level of abstraction and stay stdlib-compatible
  • go-chi/render — Companion library for JSON/XML response marshaling—commonly paired with chi for REST APIs
  • go-chi/docgen — Auto-generates routing documentation from chi routers to JSON/Markdown—used by chi projects to publish API specs
  • go-chi/httprate — Rate-limiting middleware that integrates with chi's middleware chain for API throttling

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for middleware/content_charset.go

The file structure shows middleware/content_charset.go exists but there's no corresponding middleware/content_charset_test.go file. Several other middleware files have test coverage (compress_test.go, logger_test.go, etc.), but charset handling is missing tests. This is critical for ensuring character encoding is properly validated and set, especially for internationalization scenarios.

  • [ ] Create middleware/content_charset_test.go with test cases for valid charsets
  • [ ] Add test cases for invalid/unsupported charset handling
  • [ ] Add test cases for missing charset headers
  • [ ] Add test cases for case-insensitive charset matching
  • [ ] Run tests locally with 'go test ./middleware' to verify

Add unit tests for middleware/content_encoding.go edge cases

While middleware/content_encoding_test.go exists, the test coverage appears minimal given the complexity of encoding negotiations. Looking at the file structure, this middleware is likely used frequently but deserves more robust testing for edge cases like unsupported encodings, malformed Accept-Encoding headers, and encoding precedence/priority.

  • [ ] Review existing middleware/content_encoding_test.go for coverage gaps
  • [ ] Add test cases for multiple encodings with quality values (gzip;q=0.8, deflate;q=0.5)
  • [ ] Add test cases for wildcard encoding (/) handling
  • [ ] Add test cases for identity encoding and no-encoding scenarios
  • [ ] Add test cases for invalid/malformed Accept-Encoding headers

Add missing test file for middleware/clean_path.go

The middleware/clean_path.go file exists but has no corresponding test file (no middleware/clean_path_test.go in the file list). This middleware is critical for security and path normalization. Path handling bugs can lead to routing bypasses and security vulnerabilities, making comprehensive tests essential.

  • [ ] Create middleware/clean_path_test.go with test cases for double slashes (//path)
  • [ ] Add test cases for dot segments (./path and ../path)
  • [ ] Add test cases for trailing slashes and their preservation
  • [ ] Add test cases for encoded characters and URL decoding edge cases
  • [ ] Add test cases for mixed case paths if applicable
  • [ ] Run tests to ensure middleware properly normalizes paths without breaking routing

🌿Good first issues

  • Add test coverage for pathvalue package (see _examples/pathvalue/main.go)—currently tests may be sparse; write unit tests for URL parameter extraction and type conversion
  • Document the context.Context threading pattern: create a small guide in README or CONTRIBUTING showing how chi propagates context through middleware chains (chain.go Use() and With())
  • Build a new example under _examples/ for OpenAPI/Swagger integration—there are tools like go-chi/docgen but no complete example showing spec generation end-to-end

Top contributors

Click to expand

📝Recent commits

Click to expand
  • a54874f — Bump minimum Go to 1.23, always use request.Pattern (#1048) (JRaspass)
  • 3328d4d — Apply the stringscutprefix modernizer (#1051) (JRaspass)
  • be60b2e — Simplify chi.walk with slices.Concat (#1053) (JRaspass)
  • a36a925 — Remove last uses of io/ioutil (#1054) (JRaspass)
  • 7d93ee3 — add go 1.26 to ci (#1052) (pkieltyka)
  • 903cff2 — Propagate inline middlewares across mounted subrouters (#1049) (LukasJenicek)
  • 142fada — Use strings.ReplaceAll where applicable (#1046) (JRaspass)
  • 05f1ef7 — fix(middleware): add missing return in RouteHeaders empty check (#1045) (mahanadh)
  • 6eb3588 — middleware: harden RedirectSlashes handler (#1044) (pkieltyka)
  • de0d16e — Update comment about min Go version (#1023) (JRaspass)

🔒Security observations

The chi router framework demonstrates a solid security foundation with a straightforward security reporting process documented in SECURITY.md. The main concerns are around the outdated Go version (1.16) in the rest-example, which is no longer receiving security updates. While the core router itself appears well-designed and follows Go best practices, example applications lack explicit security headers and input validation guidance. The project maintains a good code organization with no obvious hardcoded secrets, injection vulnerabilities in core code, or Docker misconfigurations. Recommendations focus on updating Go versions, enhancing security documentation with practical examples, and ensuring HTTP security headers are applied in production deployments.

  • Medium · Outdated Go Version in Dependencies — _examples/rest/go.mod. The rest-example go.mod file specifies 'go 1.16', which was released in February 2021 and is now significantly outdated. Go 1.16 has reached end-of-life and no longer receives security patches. This exposes the project to known security vulnerabilities that have been fixed in newer Go versions. Fix: Update the Go version to 1.21 or later (preferably the latest stable release). Update all go.mod files to reflect the minimum supported Go version. Run 'go get -u' to update dependencies and regenerate go.sum.
  • Medium · Pinned Dependency Without Upper Bounds — _examples/rest/go.mod (github.com/go-chi/chi/v5 v5.0.1). The chi/v5 dependency is pinned to v5.0.1 in the rest-example. While pinning versions is good practice, without a defined upper bound (e.g., using caret or tilde constraints), security patches in minor/patch versions may not be automatically applied during dependency updates. Fix: Consider using semantic versioning constraints like 'github.com/go-chi/chi/v5 ^v5.0.1' (caret) or 'github.com/go-chi/chi/v5 ~v5.0.1' (tilde) to allow patch version updates automatically while maintaining stability. Regularly run 'go get -u ./...' to pull in security patches.
  • Low · Missing Content Security Policy and Security Headers in Examples — _examples directory (all example main.go files). The example applications (hello-world, rest, todos-resource) in the _examples directory do not appear to include security middleware for essential HTTP security headers such as Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, or Strict-Transport-Security. This could leave applications vulnerable to common web attacks. Fix: Add security middleware to all example applications. Implement headers such as: 'X-Content-Type-Options: nosniff', 'X-Frame-Options: DENY', 'X-XSS-Protection: 1; mode=block', and 'Strict-Transport-Security: max-age=31536000; includeSubDomains' using chi's middleware chain or custom middleware.
  • Low · No Input Validation Documentation for Middleware — middleware directory and documentation. While the middleware directory contains various security-related middleware (content_charset, content_type, request_size, route_headers), there is no clear documentation or examples showing developers how to properly validate and sanitize user inputs to prevent injection attacks (XSS, path traversal, etc.). Fix: Add security best practices documentation covering: input validation patterns, sanitization recommendations, and examples of secure handler implementations. Create an example demonstrating proper input validation with chi.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · go-chi/chi — RepoPilot