Skip to content

v0.6.2

Choose a tag to compare

@0mjs 0mjs released this 02 Oct 22:24
· 76 commits to dev since this release

Zinc 0.6.2

Zinc now does what your struct tags and route declarations say, with no exceptions to remember. Each change below alters behavior on purpose: check them against your app in Upgrading to 0.6.

go get github.com/0mjs/zinc@v0.6.2

The body only fills body fields

type Input struct {
	Role string `header:"X-Role"`
	Name string `json:"name"`
}

A JSON body of {"Role":"admin","name":"Ada"} used to set Role when the header was missing: encoding/json matches keys to field names. Now a field tagged only for the path, query, headers or cookies never takes a value from the body, whatever the format (JSON, XML or a configured decoder). Tag a field for both, such as header:"X-Role" json:"role", to accept either.

One way to bind

c.Bind().All reads headers and cookies, exactly as a typed handler does, so converting a plain handler to a typed one changes nothing it reads. Neither reads a GET or HEAD body, which the spec never documents.

.Status means the same everywhere

.Status(201) is the status of whatever the route writes, typed or plain, unless a handler sets another or returns an error. It used to apply only to typed handlers, while documenting 201 for every route.

CORS on a group just works

api := app.Group("/api", cors.New())

The browser's automatic preflight OPTIONS request now gets the group's CORS headers. Before, only app.Use(cors.New()) answered it. Only CORS joins the automatic response; other group middleware, such as auth, still runs only for matched routes.

A docs page by default

Every app serves a reference page at /docs, next to /openapi.json. The page uses Scalar, without its AI, MCP and developer-tools buttons, and its examples use the page's own address. Config.DocsPath moves it, and "-" turns it off. Routes() and FindRoute() list both built-in endpoints, marked Builtin.

Benchmarks

Release run on the branch tip (Apple M1 Pro, on AC, quiet). The percentage is the distance from the fastest framework, so lower is better:

Table 0.6.1 0.6.2
Frameworks, against Gin and Echo 62 of 93, 10 tied, +5.2% 63 of 93, 7 tied, +6.1%
Routers, against BunRouter and Chi 35 of 66, 9 tied, +7.3% 32 of 66, 11 tied, +7.7%
  • Binding costs 3–5% more, measured by interleaved A/B against 0.6.1: a typed create request about 40 ns, a hand-written one about 50 ns. That covers keeping parameter fields out of the body and Bind().All reading headers. A first version cost up to 35% on one scenario; a one-pass scan of the body brought it down.
  • Building an app got slower while this release was in review: New() rendered the /docs page's template for every app, about 47 µs. The first release run caught it. The page now renders on its first request, and New() takes 0.68 µs.
  • Routing and the production middleware stack are unchanged.

Full changes: v0.6.1...v0.6.2