Skip to content

feat: add Zod validation to task endpoints#2

Open
W3SK3RRX wants to merge 2 commits intomasterfrom
feat/zod-validation-tasks
Open

feat: add Zod validation to task endpoints#2
W3SK3RRX wants to merge 2 commits intomasterfrom
feat/zod-validation-tasks

Conversation

@W3SK3RRX
Copy link
Copy Markdown
Collaborator

Summary

  • Replace bare TypeScript interface TaskBody with Zod schemas for runtime validation
  • Add createTaskSchema and updateTaskSchema in backend/src/schemas/task.schema.ts
  • Apply safeParse in createTask and updateTask handlers, returning 400 on invalid input

Motivation

Per CLAUDE.md rule #1: all Express request bodies must be typed with Zod schemas for runtime validation.

Test plan

  • POST /tasks with missing title returns 400 with validation details
  • POST /tasks with invalid priority value returns 400
  • PUT /tasks/:id with invalid status returns 400
  • Valid requests continue to work as before

Replace unvalidated TypeScript interface with Zod schemas for createTask and updateTask handlers, enforcing input validation at runtime per project conventions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@W3SK3RRX
Copy link
Copy Markdown
Collaborator Author

Code review

Encontrei 2 problemas:

  1. dueDate com datetime({ offset: true }) rejeita o formato enviado pelo frontend — o <input type="date"> em Tasks.tsx (linha 308) produz strings YYYY-MM-DD, mas o schema exige ISO 8601 com timezone obrigatório (ex: 2026-04-17T00:00:00Z). Qualquer criação ou edição de tarefa com prazo preenchido retornará 400.

priority: z.enum(['simple', 'medium', 'critical']).optional().default('medium'),
dueDate: z.string().datetime({ offset: true }).optional(),
category: z.string().max(100).optional(),

Correção sugerida: substituir z.string().datetime({ offset: true }) por z.string().date() (Zod 3.23+) ou z.coerce.date(), alinhando com o que o <input type="date"> produz.

  1. description: "" passa na validação Zod mas é silenciosamente ignorado no update — o updateTaskSchema não define .min(1) para description, portanto string vazia é válida. Porém o spread ...(description && { description }) descarta "" por ser falsy. O usuário tenta limpar a descrição, recebe 200 OK, mas o valor antigo permanece no banco.

where: { id },
data: {
...(title && { title }),
...(description && { description }),
...(priority && { priority }),
...(status && { status }),
...(dueDate && { dueDate: new Date(dueDate) }),
...(category && { category }),
...(xpReward && { xpReward })
}

Correção sugerida: substituir description && por description !== undefined (e mesmo padrão para os demais campos), ou adicionar .min(1) ao updateTaskSchema.description para tornar o schema coerente com o que o handler efetivamente persiste.

🤖 Generated with Claude Code

- Se esta revisão foi útil, reaja com 👍. Caso contrário, reaja com 👎.

- Replace datetime({ offset: true }) with z.string().date() to accept
  YYYY-MM-DD from HTML date inputs
- Replace truthy && checks with !== undefined to allow clearing optional
  fields like description and category

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant