Files
flyer-crawler.projectium.com/src/middleware/validation.middleware.ts
Torben Sorensen 2a5cc5bb51
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 1m17s
unit test repairs
2026-01-12 08:10:37 -08:00

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);
}
};