Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 1m17s
62 lines
2.5 KiB
TypeScript
62 lines
2.5 KiB
TypeScript
// src/middleware/validation.middleware.ts
|
|
import { Request, Response, NextFunction } from 'express';
|
|
import { ParamsDictionary, Query } from 'express-serve-static-core';
|
|
import { ZodObject, ZodError, z } from 'zod';
|
|
import { ValidationError } from '../services/db/errors.db';
|
|
|
|
/**
|
|
* A middleware factory that generates a validation middleware for a given Zod schema.
|
|
* It validates the request's params, query, and body against the schema.
|
|
*
|
|
* @param schema - The Zod schema to validate against.
|
|
* @returns An Express middleware function.
|
|
*/
|
|
export const validateRequest =
|
|
(schema: ZodObject<z.ZodRawShape>) => async (req: Request, res: Response, next: NextFunction) => {
|
|
try {
|
|
// Parse and validate the request parts against the schema.
|
|
// This will throw a ZodError if validation fails.
|
|
const { params, query, body } = await schema.parseAsync({
|
|
params: req.params,
|
|
query: req.query,
|
|
body: req.body,
|
|
});
|
|
|
|
// On success, merge the parsed (and coerced) data back into the request objects.
|
|
// For req.params, we can delete existing keys and assign new ones.
|
|
Object.keys(req.params).forEach((key) => delete (req.params as ParamsDictionary)[key]);
|
|
Object.assign(req.params, params);
|
|
|
|
// For req.query in Express 5, the query object is lazily evaluated from the URL
|
|
// and cannot be mutated directly. We use Object.defineProperty to replace
|
|
// the getter with our validated/transformed query object.
|
|
Object.defineProperty(req, 'query', {
|
|
value: query as Query,
|
|
writable: true,
|
|
configurable: true,
|
|
enumerable: true,
|
|
});
|
|
|
|
// For body, direct reassignment works.
|
|
req.body = body;
|
|
|
|
return next();
|
|
} catch (error) {
|
|
if (error instanceof ZodError) {
|
|
// If it's a Zod validation error, wrap it in our custom ValidationError.
|
|
// The global errorHandler will format this into a 400 response.
|
|
// We must map Zod's issues to our custom ValidationIssue format, as Zod's
|
|
// `path` can include `symbol`, which our type does not allow.
|
|
const validationIssues = error.issues.map((issue) => ({
|
|
...issue,
|
|
// Convert any symbols in the path to strings to match our ValidationIssue type.
|
|
path: issue.path.map((p) => String(p)),
|
|
}));
|
|
|
|
return next(new ValidationError(validationIssues));
|
|
}
|
|
// For any other unexpected errors, pass them on.
|
|
return next(error);
|
|
}
|
|
};
|