Describe the feature
API routes currently have no runtime validation on incoming data. Request bodies and cookie payloads are destructured directly from JSON.parse() and request.json() without any shape or type checks. A malformed cookie or unexpected request body results in an unhandled error and a generic 500 response instead of a clean 400.
Examples:
app/src/app/api/challenges/route.ts: JSON.parse(tokenCookie) is destructured as { eventId } with no validation
app/src/app/api/auth/signup/route.ts: request.json() is destructured as { firstName, lastName, email, eventCode } with no validation
- Same pattern across awards, leaderboard, webhook, and admin routes
Proposal
Add Zod schemas to validate:
- Cookie payloads - validate
user_token and supervisor_token shape after JSON.parse()
- Request bodies - validate POST/PATCH payloads in signup, challenge redeem, award redeem, admin verify, and webhook routes
- Sanity query params - validate that IDs and codes have the expected format before sending queries
Benefits
- Malformed input returns a descriptive 400 instead of a cryptic 500
- Schemas serve as documentation for the expected data shapes
- Catches bugs earlier (e.g., missing fields, wrong types, empty strings)
- Pairs well with the existing TypeScript types in
src/types/ by providing runtime enforcement
Scope
- Define shared schemas in a
src/lib/schemas/ directory (e.g., cookiePayload, signupBody, challengeRedeemBody)
- Add validation at the top of each API route handler
- Return structured error responses on validation failure
- We could (and should) approach this step by step instead of outright migrating everything (so we can already benefit of using Zod)
Additional information
Final checks
Describe the feature
API routes currently have no runtime validation on incoming data. Request bodies and cookie payloads are destructured directly from
JSON.parse()andrequest.json()without any shape or type checks. A malformed cookie or unexpected request body results in an unhandled error and a generic 500 response instead of a clean 400.Examples:
app/src/app/api/challenges/route.ts:JSON.parse(tokenCookie)is destructured as{ eventId }with no validationapp/src/app/api/auth/signup/route.ts:request.json()is destructured as{ firstName, lastName, email, eventCode }with no validationProposal
Add
Zodschemas to validate:user_tokenandsupervisor_tokenshape afterJSON.parse()Benefits
src/types/by providing runtime enforcementScope
src/lib/schemas/directory (e.g.,cookiePayload,signupBody,challengeRedeemBody)Additional information
Final checks