Skip to content

Port seed's strict JSON decode helpers to stem #320

Description

@krisarmstrong

Follow-up from the Zod-replacement epic. Stem received valibot + UI schemas + generated types + validator/v10, but did NOT get the matching Go-side strict JSON decode helper that seed#1131 introduced.

Gap

Seed has internal/api/decode.go exposing:

  • decodeJSONStrict(w, r, dst, max, logger) — strict mode (DisallowUnknownFields) + size limit, writes a standard JSON envelope error on failure
  • decodeJSONStrictLocalized(w, r, dst, max, logger, localizer) — same, with i18n error strings
  • decodeJSONStrictLocalizedWith(w, r, dst, max, logger, localizer, logAttrs...) — variant that carries extra structured log attrs (used by auth/MFA endpoints for client_ip, username, factor)

Stem's internal/api/ handlers currently roll their own json.Decoder + DisallowUnknownFields ad-hoc, which means:

  1. Inconsistent error envelope shapes across endpoints
  2. Easy to forget MaxBytesReader on new handlers
  3. No localized error messages

Scope

  1. Create internal/api/decode.go mirroring seed's three helpers (adjust for stem's existing logger/i18n/error envelope conventions).
  2. Sweep all internal/api/handlers_*.go files; convert ad-hoc decoders to the new helpers.
  3. Preserve any handler that needs a non-envelope error shape — leave inline strict mode + size limit but keep the existing error path.
  4. Add internal/api/decode_test.go matching seed's test coverage (strict mode rejects unknown fields, MaxBytesReader truncates, i18n variants render localized strings).

Out of scope

  • Schema-driven DTO generation (already done via the json-schema pipeline).
  • New endpoints; this is a pure refactor.

Acceptance

  • make lint && make test clean.
  • No handler in internal/api/ uses raw json.NewDecoder(r.Body) without going through a strict helper (grep gate in CI optional).
  • Closes the seed/stem parity gap from the Zod-replacement epic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions