|
1 | | -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ |
2 | | -/* eslint-disable @typescript-eslint/no-explicit-any */ |
3 | 1 | import type { SchemaInfer } from '@shared/utils' |
4 | 2 | import type { RequestHandler } from 'express' |
5 | 3 | import type { ParsedQs } from 'qs' |
6 | 4 | import zod from 'zod' |
| 5 | +import { logger } from './logger' |
7 | 6 |
|
8 | 7 | // Enforce proper typing of params and query inputs |
9 | 8 | type ZodParamsShape = { |
@@ -56,41 +55,49 @@ export function zodValidate< |
56 | 55 | body?: BodyShape |
57 | 56 | query?: QueryShape |
58 | 57 | params?: ParamsShape |
| 58 | +// eslint-disable-next-line @typescript-eslint/no-explicit-any |
59 | 59 | }): RequestHandler<ShapeOrUndefined<ParamsShape>, any, ShapeOrUndefined<BodyShape>, ShapeOrUndefined<QueryShape>> { |
60 | 60 | return (req, res, next) => { |
| 61 | + const errors: Record<string, unknown> = {} |
61 | 62 | if (schema.params) { |
62 | 63 | const output = zod.object(schema.params).safeParse(req.params) |
63 | | - if (!output.success) { |
64 | | - res.status(422).json(zod.treeifyError(output.error)) |
65 | | - return |
| 64 | + if (output.success) { |
| 65 | + req.params = output.data as ShapeOrUndefined<ParamsShape> |
| 66 | + } else { |
| 67 | + errors.params = zod.treeifyError(output.error) |
66 | 68 | } |
67 | | - req.params = output.data as any |
68 | 69 | } else { |
69 | | - req.params = undefined as any |
| 70 | + req.params = undefined as ShapeOrUndefined<ParamsShape> |
70 | 71 | } |
71 | 72 |
|
72 | 73 | // Needed to make req.query writable |
73 | 74 | Object.defineProperty(req, 'query', { ...Object.getOwnPropertyDescriptor(req, 'query'), value: req.query, writable: true }) |
74 | 75 | if (schema.query) { |
75 | 76 | const output = zod.object(schema.query).safeParse(req.query) |
76 | | - if (!output.success) { |
77 | | - res.status(422).json(zod.treeifyError(output.error)) |
78 | | - return |
| 77 | + if (output.success) { |
| 78 | + req.query = output.data as ShapeOrUndefined<QueryShape> |
| 79 | + } else { |
| 80 | + errors.query = zod.treeifyError(output.error) |
79 | 81 | } |
80 | | - req.query = output.data as any |
81 | 82 | } else { |
82 | | - req.query = undefined as any |
| 83 | + req.query = undefined as ShapeOrUndefined<QueryShape> |
83 | 84 | } |
84 | 85 |
|
85 | 86 | if (schema.body) { |
86 | 87 | const output = zod.object(schema.body).safeParse(req.body) |
87 | | - if (!output.success) { |
88 | | - res.status(422).json(zod.treeifyError(output.error)) |
89 | | - return |
| 88 | + if (output.success) { |
| 89 | + req.body = output.data as ShapeOrUndefined<BodyShape> |
| 90 | + } else { |
| 91 | + errors.body = zod.treeifyError(output.error) |
90 | 92 | } |
91 | | - req.body = output.data as any |
92 | 93 | } else { |
93 | | - req.body = undefined as any |
| 94 | + req.body = undefined as ShapeOrUndefined<BodyShape> |
| 95 | + } |
| 96 | + |
| 97 | + if (errors.params || errors.query || errors.body) { |
| 98 | + res.status(422).json(errors) |
| 99 | + logger.debug({ message: 'API Validation failed', path: req.path, method: req.method, error: errors }) |
| 100 | + return |
94 | 101 | } |
95 | 102 |
|
96 | 103 | next() |
|
0 commit comments