Skip to content
Snapshot of the v0.1 release. Fixes and additions since then are not in it. Current documentation →

v0.2.0

Released 20 August 2026.

Pre-alpha. Everything below is implemented and tested, but the API may change without notice and there is no compatibility guarantee yet.

Install

go install github.com/caspel26/goninja/cmd/goninja@v0.2.0
go get github.com/caspel26/goninja@v0.2.0

Requires 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, CodedErrorerror 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.

Links