Defining Schemas with Zod & Inferring Types
With the project running, model the actual data the API will accept and return, starting from Zod schemas instead of hand-written interfaces.
Each field reads like a description of the constraint, z.string().min(1).max(200) isn't just "a string," it's "a string between 1 and 200 characters," checked for real when .parse() runs, not just documented in a comment.
Deriving request schemas from the base schema
.omit() and .partial() on a Zod schema work exactly like Omit<T, K> and Partial<T> from the intermediate TypeScript course, except they reshape the runtime validator, not just the compile-time type, and z.infer immediately gives you the matching TypeScript type for free.
Custom validation with refine
.refine() adds a check .min()/.max()/.enum() can't express on their own, an arbitrary function returning true/false, with a custom error message attached to a specific field.
NOTE
Notice there's still only one place Task's shape is written down, the TaskSchema. CreateTaskInput, UpdateTaskInput, and the runtime checks for all three all derive from it, change one field once and everything downstream stays in sync automatically.