feat(api): zod schema error messages → i18n catalog keys (#212) #246
No reviewers
Labels
No labels
area:auth
area:ci
area:db
area:infra
area:native
area:pwa
area:service
epic
feature
foundation
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
james/carol!246
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "212-zod-i18n"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Resolves ADR-0025 §6 by moving every zod validator off inline English messages onto a small, finite
ZodIssueCode → catalog keymap. The server'sProblemFieldErrornow carries the catalog key + args alongside the legacy message; the universal client renders through a newformatApiErrorhelper and flips with the active locale.Mapping
ZodIssueCodeinvalid_typeerrors.zod.invalidType{ expected }too_smallminimum <= 1errors.zod.requiredtoo_smallminimum > 1errors.zod.tooShort{ min }too_bigerrors.zod.tooLong{ max }invalid_formatformat: emailerrors.zod.invalidEmailinvalid_formatformat: urlerrors.zod.invalidUrlinvalid_formatformat: datetimeerrors.zod.invalidDateTimeinvalid_formaterrors.zod.invalidFormatinvalid_valueerrors.zod.invalidValueunrecognized_keyserrors.zod.unrecognizedKeyscustomparams.keyparamserrors.zod.invalidThe helper is wired up globally via
z.config({ customError })so everysafeParse(...)across the codebase resolves to a key without per-route opt-in..refine()cases surface their key via{ params: { key: "errors.foo.bar" } }.Files swept
apps/api/lib/api/zodErrorMap.ts,apps/api/tests/lib/api/zodErrorMap.test.ts,apps/client/lib/api/formatApiError.ts,apps/client/tests/formatApiError.test.ts.lib/dto/{auth,auth-token,education,job,note,pat,profile,settings,skill,user}.ts— every inline.min/.max/.regex/.email/.refinemessage removed;.refine()annotated withparams: { key }.lib/api/errors.tsbuilds the new field-error shape;lib/api/openapi.tsextendsProblemFieldErrorwithcode/key/args.login.tsx,register.tsx,(app)/notes.tsxrender viaformatApiError.errors.zod.*namespace added topackages/i18n/messages/{en,es}.json.CLAUDE.mdi18n bullet refreshed; ADR-0025 §6 marked resolved by #212;docs/api-conventions.mdexample + field doc updated.openapi.json+packages/api-client/src/generated/schema.d.ts.Test plan
pnpm -F @carol/api test— 568 passing (zodErrorMap unit tests + extended notes / auth integration tests)pnpm -F @carol/api typecheck/lintpnpm -F @carol/api openapi:check/openapi:coveragepnpm -F @carol/api-client test/typecheck/lint/checkpnpm -F @carol/client test/typecheck/lint/export:webapps/client/tests/formatApiError.test.ts— asserts the visible string flips when the locale changes (en → es)Links: #212, ADR-0025 §6.
🤖 Generated with Claude Code
Trivy (container image)
Threshold:
high· Total findings: 121 · At/above threshold: 16.27.0, 7.28.0, 8.5.0📊 Test coverage
Patch coverage: no testable lines changed.
Overall (
app/,lib/,db/, excluding UI per ADR-0019):Soft thresholds per ADR-0019. Coverage is informational and does not block merge.