Files
crewli/dev-docs/ARCH-API-VALIDATION.md

6.7 KiB
Raw Permalink Blame History

ARCH-API-VALIDATION — API Response Validation

Status: Skeleton / placeholder. Full content to be written when this workstream is scheduled. Scope: Workstream-sized architectural concern. Not part of any active sprint. Last updated: 2026-05-05 (placeholder)


1. Purpose

This document will define Crewli's uniform approach to typed, runtime-validated contracts at the backend ↔ frontend API boundary.

The goal is to eliminate three classes of risk that currently exist:

  1. Backend → frontend response drift. Backend API resources emit JSON shapes that frontend composables consume via TypeScript interface declarations. The interfaces are written by hand, may drift from the resource emit, and there is no runtime check that catches drift before it reaches the user.
  2. Frontend type-erosion at API boundaries. Once a response is parsed by axios, all type information is asserted (as ResponseType) rather than verified. A backend change that adds, removes, or restructures a field passes through TypeScript silently.
  3. String-literal fields without canonical enumeration. Domain values like 'portal' | 'organizer' (auth context), 'ok' | 'failed' | null (apply-status), purpose names, and similar appear as string-unions across composables without a single source of truth bridging backend and frontend.

The PR-B2a contexts.{available, default} block on /auth/me made this concrete: the value is a string emitted from PHP, consumed as a string-union in TypeScript, with no shared enum or runtime guard.

2. Architectural drivers

This workstream is justified by the following enterprise-quality principles:

  • No half-measures. A frontend-only TypeScript Enum without backend enumeration creates a mixed pattern where some fields are typed-and-validated, others are not. Either we validate API responses uniformly or we do not.
  • Single source of truth. Backend PHP Enums are the canonical definition. Frontend types and Zod schemas derive from the backend, ideally via codegen.
  • Defense at the boundary. Runtime validation at API ingress catches drift the moment it occurs, with a clear error, instead of silently propagating bad data into stores and components.
  • No technical debt forwarded into S3b. The Form Builder UI workstream (RFC-FORM-BUILDER-UI) will introduce a new wave of API-consuming composables. Those should land on the validated pattern from day one, not retrofit later.

3. Scope

3.1 Backend

  • Audit all App\Http\Resources\** for emitted enumerated values (auth contexts, statuses, role categories, purpose names, etc.)
  • For each: introduce a backing PHP Enum under App\Enums\** if not present; resource emits ->value (preserves JSON wire format)
  • Document the canonical enum list per domain area (auth, form-builder, accreditation when it lands, etc.)
  • TBD: codegen pipeline that exports a JSON or TypeScript representation of these enums for frontend consumption

3.2 Frontend

  • Adopt zod (already a project dependency via VeeValidate) for runtime schema validation at API response ingress
  • Convention: every composable under apps/app/src/composables/api/** parses its response through a Zod schema before returning typed data
  • TypeScript types are inferred from Zod schemas via z.infer<typeof Schema> — no hand-written interfaces for API response shapes
  • Codegen pipeline that derives Zod schemas from backend resources (candidates: Scramble OpenAPI output → openapi-zod-client or similar)
  • Migration sequence: high-traffic / high-risk endpoints first (/auth/me, form-builder list endpoints, identity-match endpoints), then full sweep

3.3 Tooling

  • One ARCH document (this file, fully written) defining the convention with examples
  • One reference implementation showing the round-trip: backend Enum → resource → codegen output → Zod schema → composable → typed return
  • CI gate that fails the build if any composable under composables/api/** returns un-validated data (lint rule or convention test)
  • Migration tracker (issue per composable until full coverage)

4. Out of scope

  • Form input validation (already handled by VeeValidate + Zod for form fields — this workstream is about API responses, not forms)
  • WebSocket / real-time message validation (separate concern, addressed when COMM-01 lands)
  • Public API contracts for third-party consumers (separate workstream — DIFF-03 / OpenAPI publication)

5. Sequencing

This workstream lands after WS-3 PR-C (cleanup) and after WS-7 (GlitchTip observability), and before RFC-FORM-BUILDER-UI begins implementation. Rationale:

  • PR-C clears the consolidation backlog so this workstream lands on a stable frontend layout
  • WS-7 provides the observability surface to monitor validation-failure rates after rollout (Sentry-compatible exception capture catches Zod parse failures cleanly)
  • RFC-FORM-BUILDER-UI is the first major feature that will introduce a substantial set of new API composables — landing this workstream first means S3b consumes the validated pattern from its first commit

Estimated effort: 23 days for the convention + reference implementation + ~5 high-priority endpoints; further composable rollout happens organically as features are touched.

6. Open decisions (to be resolved when this workstream is scheduled)

  • Codegen toolchain. Scramble already emits OpenAPI for /dev-docs/api.openapi.yaml (per DOC-01). Can openapi-zod-client or orval consume that output to generate Zod schemas? Or do we hand-roll a small generator that walks PHP Resource classes directly?
  • Validation failure mode. Hard fail (throw, surface to error toast, capture to GlitchTip) versus soft fail (log warning, return raw data, do not block UX). Recommended starting position: hard fail in development, soft fail with capture in production. Reconsider after observing real failure rates.
  • Per-route opt-out. Are there endpoints where validation cost outweighs benefit (e.g. very large list responses with stable schemas)? If so, what's the opt-out mechanism?
  • Boundary placement. Validation in the composable, in the axios response interceptor, or in a dedicated middleware layer. Each has tradeoffs around testability and where errors surface.

7. Cross-references

  • dev-docs/AUTH_ARCHITECTURE.md — current /auth/me shape (consumer of the first validation rollout)
  • dev-docs/ARCH-FORM-BUILDER.md — form-builder API surface (high-priority migration target)
  • dev-docs/ARCH-BINDINGS.md — FormBindingApplicator pipeline emits status fields that are good early validation targets
  • dev-docs/BACKLOG.mdARCH-API-RESPONSE-VALIDATION entry for scheduling
  • RFC-FORM-BUILDER-UI (forthcoming) — first feature workstream that will consume the validated pattern from day one