Skip to content

Commit

Permalink
add validation with zod
Browse files Browse the repository at this point in the history
  • Loading branch information
willpinha committed May 8, 2024
1 parent 111e170 commit 6fe522b
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 7 deletions.
13 changes: 10 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"express": "^4.19.2",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0",
"tsx": "^4.9.3"
"tsx": "^4.9.3",
"zod": "^3.23.7"
},
"devDependencies": {
"@types/cookie-parser": "^1.4.7",
Expand Down
17 changes: 17 additions & 0 deletions src/controllers/hello.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { RequestHandler } from "express";
import { z } from "zod";
import { JSend } from "@/utils/jsend";
import { getData } from "@/services/hello.controller";
import { validateBody } from "@/utils/validation";

const controller: RequestHandler = async (req, res, next) => {
const bodySchema = z.object({ id: z.string(), foo: z.string().optional() });

const { id } = await validateBody(req, bodySchema);

const data = getData(id);

res.status(200).json(JSend.success(data));
};

export default controller;
2 changes: 1 addition & 1 deletion src/controllers/swagger.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const specOptions = {

const uiOptions = {
swaggerOptions: {
url: "/docs/swagger.json",
url: "/api/docs/swagger.json",
},
};

Expand Down
4 changes: 2 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ const app = express();

app.use("/public", express.static("public"));

app.use("/api", routes);

app.use(express.json());
app.use(express.text());
app.use(express.urlencoded({ extended: true }));
Expand All @@ -22,6 +20,8 @@ app.use(
})
);

app.use("/api", routes);

app.listen(3000, () => {
console.log("Server is running on port 3000");
});
5 changes: 5 additions & 0 deletions src/middlewares/foo.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { RequestHandler } from "express";

const handler: RequestHandler = (req, res, next) => {};

export default handler;
2 changes: 2 additions & 0 deletions src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import {
swaggerServe,
swaggerSetup,
} from "@/controllers/swagger.controller";
import helloController from "@/controllers/hello.controller";

// Middlewares

const router = express.Router();

router.get("/docs/swagger.json", (req, res) => res.json(swaggerSpec));
router.use("/docs", swaggerServe, swaggerSetup);
router.post("/hello", helloController);

export default router;
7 changes: 7 additions & 0 deletions src/services/hello.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function getData(id: string) {
return {
id,
foo: "bar",
hello: "world",
};
}
19 changes: 19 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
declare global {
export type JSendResponse<T> =
| {
status: "success";
data: T;
}
| {
status: "fail";
data: T;
}
| {
status: "error";
message: string;
code?: number;
data?: T;
};
}

export default {};
36 changes: 36 additions & 0 deletions src/utils/jsend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* JSend is a specification that gives a consistent structure for JSON responses. Make sure to use
* this in all of your JSON responses in controllers
*
* See the JSend specification for more information
*
* {@link https://github.com/omniti-labs/jsend}
*/
export namespace JSend {
export function success<T>(data: T): JSendResponse<T> {
return {
status: "success",
data,
};
}

export function fail<T>(data: T): JSendResponse<T> {
return {
status: "fail",
data,
};
}

export function error<T>(
message: string,
code?: number,
data?: T
): JSendResponse<T> {
return {
status: "error",
message,
code,
data,
};
}
}
32 changes: 32 additions & 0 deletions src/utils/validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Request } from "express";
import { z } from "zod";

export class ValidationError extends Error {
public readonly formattedError: any;

constructor(formattedError: any) {
super();
this.formattedError = formattedError;
}
}

async function validate<T extends z.ZodSchema>(
schema: T,
value: any
): Promise<Required<z.infer<T>>> {
const { success, error, data } = await schema.safeParseAsync(value);

if (!success) {
throw new ValidationError(error.format());
}

return data;
}

export async function validateBody(req: Request, schema: z.ZodSchema) {
return await validate(schema, req.body);
}

export async function validateQuery(req: Request, schema: z.ZodSchema) {
return await validate(schema, req.query);
}

0 comments on commit 6fe522b

Please sign in to comment.