Zinc
Galvanize net/http.
The high-performance, minimalist web layer for Go. Routing, binding, errors, and 29 middleware, applied as a thin coat over the standard library you already trust.
- /usersstatic
- /42{id}
- /postsstatic
- /7{post}
- /
- healthhealth
- users
- mecurrentUser
- {id} = "42"showUser
- posts
- {post} = "7"showPost
- files
- {path...}serveFile
app.Get("/users/{id}/posts/{post}", showPost)c.Param("id") // "42"c.Param("post") // "7"// Literals compare without case; captured values keep theirs.
What you get
Everything a service needs. Nothing it doesn't.
Follow one request through Zinc, from the first middleware to the response.
app.Use(
middleware.RequestID(),
middleware.RequestLogger(),
middleware.Recover(),
middleware.Secure(),
)
api := app.Group("/api", middleware.ContextTimeout(5*time.Second))L2Every response carries an ID that your logs share.Guide →
GET /api/users/42
200 OK
api := app.Group("/api", requireAuth)
api.Get("/users/{id}", func(c *zinc.Context) error {
return c.JSON(zinc.Map{"id": c.Param("id")})
})
app.RouteNotFound("/api/{tail...}", apiNotFound)L4{id} is captured from the path and read with c.Param.Guide →
GET /api/users/42
200 OK
type CreateUser struct {
Name string `json:"name"`
Email string `json:"email"`
}
app.Post("/users", func(c *zinc.Context) error {
var in CreateUser
if err := c.Bind().JSON(&in); err != nil {
return zinc.ErrBadRequest.WithCause(err)
}
return c.Status(zinc.StatusCreated).JSON(in)
})L8Decodes the body into CreateUser; bad input becomes a 400.Guide →
POST /users {"name":"Ada","email":"ada@example.com"}
201 Created
app.Get("/users/{id}", func(c *zinc.Context) error {
user, err := users.Find(c.Param("id"))
if err != nil {
return zinc.ErrNotFound.WithMessage("user not found")
}
return c.JSON(user)
})L4Return an error; the error handler writes the response.Guide →
GET /users/nope
404 Not Found
How it fits
A thin coat, not a new engine.
Galvanizing protects steel without replacing it. Zinc does the same for net/http: your app is still an http.Handler, and standard handlers, middleware, and servers plug straight in.
Pick one to see where it plugs into the stack.
- 1Run it on a server you configure
server.Handler = app - 2Serve a route with any standard handler
app.HandleHTTP("GET /metrics", promhttp.Handler()) - 3Wrap the app in standard middleware
app.UseHTTP(otelhttp.NewMiddleware("api"))
zinc.HandlerFunchttp.Handler
Measured, in the repo
60/ 77
Scenarios where Zinc recorded the lowest latency against Gin, Echo, and Chi. Gin led 12, Chi 3, and Echo 2. Zinc was measured on 25 September; rival samples are from 24 September.
Zinc fastestA rival fastest
Mixed-date comparison25 September 2026Apple M1 Pro · go1.27.1 · commit 42e11d2
Full benchmark report →HelloWorldns/op · lower is faster
Middleware
A periodic table of production parts.
Numbered in chain order: each part wraps everything after it. Select parts to galvanize them into a stack, and the table writes the app.Use call in the right order.
Your compound
RiLgRcSh
app.Use(
middleware.RequestID(),
middleware.RequestLogger(),
middleware.Recover(),
middleware.Secure(),
)Coat your first service.
Start a new API in a few minutes, or add Zinc to a service you already run.