From 628315f4efa50247c79dc7efe68dd4ffcc271f93 Mon Sep 17 00:00:00 2001 From: "LAPTOP-S0QU96IH\\david" Date: Tue, 8 Aug 2023 09:29:28 +0200 Subject: [PATCH] feat(validation): added model-state-validation to the template --- package.json | 3 ++- src/Dtos/HelloWorldDto.ts | 14 ++++++++++++++ src/Dtos/index.ts | 1 + src/controllers/helloWorld.ts | 11 ++++++++++- src/enums/ServerCode.ts | 11 +++++++++++ src/enums/index.ts | 1 + src/index.ts | 2 ++ src/middleware/index.ts | 1 + src/middleware/modelValidation.ts | 22 ++++++++++++++++++++++ src/routes/helloWorldRouter.ts | 7 +++++++ yarn.lock | 5 +++++ 11 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 src/Dtos/HelloWorldDto.ts create mode 100644 src/Dtos/index.ts create mode 100644 src/enums/ServerCode.ts create mode 100644 src/enums/index.ts create mode 100644 src/middleware/index.ts create mode 100644 src/middleware/modelValidation.ts diff --git a/package.json b/package.json index 8f4f1f2..10004b0 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "mysql2": "^3.5.2", "reflect-metadata": "^0.1.13", "sequelize": "^6.32.1", - "sequelize-typescript": "^2.1.5" + "sequelize-typescript": "^2.1.5", + "zod": "^3.21.4" }, "devDependencies": { "@types/dotenv": "^8.2.0", diff --git a/src/Dtos/HelloWorldDto.ts b/src/Dtos/HelloWorldDto.ts new file mode 100644 index 0000000..5fc1424 --- /dev/null +++ b/src/Dtos/HelloWorldDto.ts @@ -0,0 +1,14 @@ +import { z } from "zod"; + +export type HelloWorldDto = { + name: string; +}; + +export const helloWorldDTOSchema = z.object({ + body: z.object({ + name: z.string({ + required_error: "Name is required", + description: "Name of the world", + }), + }), +}); diff --git a/src/Dtos/index.ts b/src/Dtos/index.ts new file mode 100644 index 0000000..d5c1e60 --- /dev/null +++ b/src/Dtos/index.ts @@ -0,0 +1 @@ +export * from "./HelloWorldDto"; diff --git a/src/controllers/helloWorld.ts b/src/controllers/helloWorld.ts index cd9ed14..874a477 100644 --- a/src/controllers/helloWorld.ts +++ b/src/controllers/helloWorld.ts @@ -1,8 +1,17 @@ import { Request, Response } from "express"; +import { HelloWorldDto } from "../Dtos"; // Placeholder code for this template const get = async (_: Request, res: Response): Promise => { res.status(200).send("Hello World"); }; -export const helloWorld = { get }; +const create = async ( + req: Request, + res: Response +): Promise => { + const { name } = req.body; + res.send(`Hello ${name}`); +}; + +export const helloWorld = { get, create }; diff --git a/src/enums/ServerCode.ts b/src/enums/ServerCode.ts new file mode 100644 index 0000000..9a8632a --- /dev/null +++ b/src/enums/ServerCode.ts @@ -0,0 +1,11 @@ +export enum ServerCode { + Ok = 200, + Created = 201, + Unauthorized = 401, + Forbidden = 403, + NotFound = 404, + BadRequest = 400, + Conflict = 409, + InternalServerError = 500, + } + \ No newline at end of file diff --git a/src/enums/index.ts b/src/enums/index.ts new file mode 100644 index 0000000..996910d --- /dev/null +++ b/src/enums/index.ts @@ -0,0 +1 @@ +export * from './ServerCode'; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 3a77cdc..67b3807 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import express, { Express } from "express"; +import bodyParser from "body-parser"; import "./config/env"; import { DBConnection } from "./config/database"; import { routes } from "./routes"; @@ -6,6 +7,7 @@ import { routes } from "./routes"; const app: Express = express(); const port = process.env.PORT; +app.use(bodyParser.json()); app.use("/v1", routes); const start = async (): Promise => { diff --git a/src/middleware/index.ts b/src/middleware/index.ts new file mode 100644 index 0000000..be95241 --- /dev/null +++ b/src/middleware/index.ts @@ -0,0 +1 @@ +export * from './modelValidation'; \ No newline at end of file diff --git a/src/middleware/modelValidation.ts b/src/middleware/modelValidation.ts new file mode 100644 index 0000000..ffaa126 --- /dev/null +++ b/src/middleware/modelValidation.ts @@ -0,0 +1,22 @@ +import { Request, Response, NextFunction } from "express"; +import { AnyZodObject } from "zod"; +import { ServerCode } from "../enums/ServerCode"; + +// Middleware to validate the request body, query and params against a schema +const validateModel = + (schema: AnyZodObject) => + async (req: Request, res: Response, next: NextFunction): Promise => { + try { + await schema.parseAsync({ + body: req.body, + query: req.query, + params: req.params, + }); + return next(); + } catch (error) { + res.status(ServerCode.BadRequest).json(error); + return; + } + }; + +export { validateModel }; diff --git a/src/routes/helloWorldRouter.ts b/src/routes/helloWorldRouter.ts index 17c0763..b059a58 100644 --- a/src/routes/helloWorldRouter.ts +++ b/src/routes/helloWorldRouter.ts @@ -1,8 +1,15 @@ import express from "express"; import { helloWorld } from "../controllers/helloWorld"; +import { validateModel } from "../middleware"; +import { helloWorldDTOSchema } from "../Dtos"; const helloWorldRouter = express.Router(); helloWorldRouter.get("/", helloWorld.get); +helloWorldRouter.post( + "/create", + validateModel(helloWorldDTOSchema), + helloWorld.create +); export { helloWorldRouter }; diff --git a/yarn.lock b/yarn.lock index 3b5a009..0b37f59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1267,3 +1267,8 @@ yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +zod@^3.21.4: + version "3.21.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db" + integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==