alice
A minimalist Go library for chaining HTTP middleware into a single handler.
Repository Health
Technical Analysis
Alice is a small, dependency-free Go package that replaces deeply nested middleware calls like Middleware1(Middleware2(Middleware3(App))) with a single readable chain: alice.New(Middleware1, Middleware2, Middleware3).Then(App). It doesn’t invent its own middleware type — any function shaped like func(http.Handler) http.Handler already satisfies Alice’s Constructor type, so existing middleware from other packages works without adapters.
Chains built with Alice are immutable: Append and Extend always return a new chain rather than mutating the original, which makes it safe to define a shared base chain (logging, recovery, rate limiting) and branch off route-specific variants without accidentally affecting the shared base. The whole library is a single file of well-documented Go with zero runtime dependencies.
What You Get
- A
Chaintype andNew()constructor that stores an ordered list of middleware constructors without invoking them untilThenis called ThenandThenFuncmethods that wrap a final handler (or handler function) in reverse constructor order, treating nil ashttp.DefaultServeMuxAppendandExtendmethods for building new, immutable chains from an existing one — for shared base chains and route-specific branching- Zero external dependencies — the entire library builds on the Go standard library’s
net/httppackage alone
Common Use Cases
- Standardizing the middleware stack (logging, recovery, auth) applied consistently across every route in a Go HTTP service
- Branching a shared base chain into route-group-specific variants (e.g., adding admin-only auth) without touching the shared base
- Replacing manually nested middleware calls (
m1(m2(m3(h)))) with a single readable chain declaration - Composing third-party middleware constructors (rate limiters, CSRF protection) alongside custom middleware in one ordered chain
Under The Hood
Architecture The entire library lives in a single file, chain.go, exposing one type, Chain, wrapping a slice of Constructor values, where Constructor is func(http.Handler) http.Handler. New copies the given constructors into an immutable slice; Then and ThenFunc apply them in reverse order via a simple loop, so the first constructor passed to New becomes the outermost handler in the resulting call chain, with a nil handler treated as http.DefaultServeMux. Append and Extend never mutate the receiver — each allocates a new backing slice and copies both sets of constructors into it, so chains can be shared as a common base and branched without aliasing bugs. There is no dependency injection, no reflection-based dispatch, and no internal state beyond the constructor slice — the abstraction really is just a for loop, as the README itself claims.
Tech Stack The module is github.com/justinas/alice, declaring a go 1.12 directive, with zero external dependencies — the only import anywhere in the package is the standard library’s net/http. Build tooling is minimal: a .travis.yml runs go test across an extensive matrix of historical Go versions and architectures, there is no go.sum (nothing third-party to lock), no Makefile, no linter configuration, and no Dockerfile. This is a bare, importable library rather than a deployable service, with a tech stack that begins and ends at the standard net/http package.
Code Quality chain_test.go provides straightforward unit tests using the standard testing package and httptest, exercising New, Then, ThenFunc, Append, and Extend, including explicit immutability checks (asserting the underlying constructor slices are distinct after Append/Extend) and correct execution order via a tagMiddleware helper that writes an identifying tag into the response body. Error handling is minimal because there is little that can fail: the only defensive behavior is treating a nil http.Handler as http.DefaultServeMux. Naming is idiomatic Go — short receivers, Then/ThenFunc mirroring the standard library’s Handler/HandlerFunc pattern — and every exported symbol carries a doc comment with runnable-looking usage examples. CI relies on an aging Travis configuration testing across many Go versions rather than any modern linting gate, and no linter/formatter configuration is checked into the repo.
API Design Alice’s public surface is deliberately tiny: one type and four methods, chosen to mirror stdlib naming (Then/ThenFunc echoing Handler/HandlerFunc) so the learning curve is close to zero for anyone already comfortable with net/http. Getting started requires no boilerplate: any middleware written in the ordinary Go decorator style already satisfies Constructor, so alice.New(mw1, mw2).Then(handler) works immediately with existing code, unlike frameworks that require adopting a bespoke middleware type. Doc comments illustrate reuse patterns — shared base chains, Append for route-specific extensions — directly inline, and dropping Alice later for manual nesting or another chaining library requires no changes to the middleware functions themselves. The tradeoff for this minimalism is a total absence of extras: no per-route metadata, no built-in logging/recovery middleware, and no context-based short-circuiting API, all of which must come from user-supplied middleware or another package.
Used by 3 apps in this directory
opencloud
File Storage
Open source file management and collaboration platform that keeps your data under your control, no database required.
PrivateCaptcha
Security · Authentication
Privacy-first, self-hostable Proof-of-Work CAPTCHA for GDPR-compliant bot protection.
Tyk API Gateway
Developer Tools · Devops
Cloud-native, high-performance open-source API gateway for REST, GraphQL, gRPC, and TCP — built in Go since 2014 with no feature lockout.