v0.2.0
Released 20 August 2026.
Install
go install github.com/caspel26/goninja/cmd/goninja@v0.2.0
go get github.com/caspel26/goninja@v0.2.0Requires Go 1.25 or newer.
Added
goninja.CodedError and an optional Code field
NotFound, ValidationError, BadRequest, and the new Unauthorized
(below) all implement a new interface, CodedError — error plus
ErrorCode() string. Left unset, each type keeps its existing default JSON
"code" (NOT_FOUND, VALIDATION_FAILED, BAD_REQUEST, UNAUTHORIZED).
Setting Code on the error value lets a specific failure carry a more
precise, machine-readable identifier than the HTTP status alone provides —
the same idea as a Stripe or Google API error code sitting next to the
status. The new order-validation error (below) is the first concrete user
of it:
return goninja.BadRequest{
Detail: "cannot order by \"" + field + "\"",
Code: "INVALID_ORDER_FIELD",
}DefaultErrorMapper resolves the body’s "code" field by calling
ErrorCode(), so a custom ErrorMapper gets the same default-or-override
behavior by doing the same.
See Errors & Responses.
goninja.Unauthorized
A new framework error type, mapped to 401 by DefaultErrorMapper. Every
configured Authenticator declining a request now goes through the same
Respond path as every other framework error, so the response is a JSON
body ({"code":"UNAUTHORIZED","error":"unauthorized"}) consistent with
404/422/400/500 — previously it was a plain-text http.Error response,
the one inconsistency left in the error-handling story.
See Errors & Responses and Authentication.
Changed
The generator rejects models it cannot turn into working code
Previously, a model missing a field named ID, an ID typed something
other than int64/string, a pointer relation field, byid on a
non-relation field, or filter on a relation field would still generate a
file — one that failed to compile, with no message pointing back at the
actual model responsible. goninja generate now validates every model
before writing anything, reports every problem across every model in a
single run, and writes zero files if any model is rejected:
$ goninja generate -models-import myapp/models
goninja: codegen: models/book.go: Book: no goninja-tagged field named ID; every
model needs one, typed int64 or string, and it must carry a goninja tag to be
exposed (e.g. `goninja:"list,retrieve"`)
See Struct Tags and the CLI reference.
An unrecognized ?order= field is now a 400
Previously an unknown ordering field fell through silently and returned the
default order with a 200 — a response indistinguishable from a correctly
sorted one, which made a typo in a query string invisible. It’s now
rejected while parsing the query string, before List ever runs, with the
same column whitelist that already made ordering injection-safe:
{ "code": "INVALID_ORDER_FIELD", "error": "cannot order by \"titel\"" }See Filtering, Ordering & Pagination.
Versioned documentation
goninja.dev now serves the current working tree at
the root and a frozen snapshot of each released minor series at /vX.Y/,
switchable from a version selector in the navbar that also shows the live
GitHub star count. This page is part of that: the
Changelog is searchable by version from the site’s own search index.
CI hardening
GitHub Actions workflow permissions are scoped per-job instead of granted
workflow-wide, closing a new_security_rating regression the project’s
SonarQube quality gate caught.