@assassinonz/exzodus-router
Version:
Express router wrapper with end to end type safety
81 lines (80 loc) • 3.58 kB
JavaScript
import express from "express";
import { z } from "zod";
import { METHODS } from "../core/index.js";
export { express, z };
export class ExZodusRouter {
constructor() {
}
static new(apiDef, config) {
const router = express.Router();
for (const method of METHODS) {
const originalHandlerRegistrar = router[method].bind(router);
router[method] = (path, ...handlers) => {
const routeDescription = apiDef[path]?.[method] ?? undefined;
if (!routeDescription) {
return originalHandlerRegistrar(path, handlers);
}
const requestValidator = (req, res, next) => {
try {
if (routeDescription?.parameters?.path) {
req.params = routeDescription.parameters.path.parse(req.params);
}
else {
req.params = z.object({}).parse(req.params);
}
if (routeDescription?.parameters?.query) {
for (const key of Object.keys(req.query)) {
if (req.query[key] === "true") {
req.query[key] = true;
}
else if (req.query[key] === "false") {
req.query[key] = false;
}
}
req.query = routeDescription.parameters.query.parse(req.query);
}
else {
req.query = z.object({}).parse(req.query);
}
if (!config.skipRequestBodyValidation) {
if (routeDescription?.request) {
req.body = routeDescription.request.parse(req.body);
}
else {
req.body = z.object({}).optional().parse(req.body);
}
}
return next();
}
catch (err) {
config.errorHandler(err, req, res);
}
};
const responseValidator = (_req, res, next) => {
const originalJson = res.json;
res.json = function (body) {
try {
const parsedBody = routeDescription.responses?.[res.statusCode]?.parse(body) ?? body;
return originalJson.call(this, parsedBody);
}
catch {
res.status(500);
return originalJson.call(this, {
status: "Internal server error",
message: "Server generated response is out of API spec"
});
}
};
return next();
};
if (config.attachResponseValidator) {
return originalHandlerRegistrar(path, requestValidator, responseValidator, handlers);
}
else {
return originalHandlerRegistrar(path, requestValidator, handlers);
}
};
}
return router;
}
}