Validating Requests and Building REST Endpoints
Combine Zod schemas, typed handlers, and the async wrapper into full, validated REST endpoints.
A validation middleware factory
safeParse never throws, it returns { success: true, data } or { success: false, error }, which suits middleware better than .parse()'s throw-based style, a bad request is an expected outcome here, not an exceptional one. <T extends z.ZodTypeAny> makes validateBody reusable for any schema, not just one hard-coded shape.
Wiring up the endpoints
Every layer does one job: validateBody rejects malformed input before the handler ever runs, asyncHandler catches anything the handler itself throws, and the handler is left to assume req.body is already a valid CreateTaskInput, because by the time it runs, Zod already checked that.
Status codes that mean something
| Code | When |
|---|---|
200 | Successful GET/PATCH |
201 | Successful POST that created something |
400 | The request body failed validation |
404 | The requested resource doesn't exist |
500 | An unexpected server error (caught by errorHandler) |
WARNING
Validating inputs (req.body, req.params) is the priority, but don't skip checks on values you construct yourself either, tasks.find(...) returning undefined is exactly the kind of case the 404 branch above exists to catch, TypeScript's strict mode will actually force you to handle it.