-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2326 from CAFECA-IO/feature/zod
Feature/zod
- Loading branch information
Showing
7 changed files
with
173 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { APIName } from '@/constants/api_connection'; | ||
import { zodExampleValidator } from '@/lib/utils/zod_schema/zod_example'; | ||
|
||
/* | ||
* Info: (20240909 - Murky) Record need to implement all the keys of the enum, | ||
* it will cause error when not implement all the keys | ||
* use code below after all the keys are implemented | ||
*/ | ||
|
||
// import { IZodValidator } from "@/interfaces/zod_validator"; | ||
// export const API_ZOD_SCHEMA: Record<APIName, IZodValidator> = { | ||
// [APIName.ZOD_EXAMPLE]: zodExampleValidator, | ||
// }; | ||
|
||
export const API_ZOD_SCHEMA = { | ||
[APIName.ZOD_EXAMPLE]: zodExampleValidator, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { z } from 'zod'; | ||
// Info: (20240909 - Murky) This interface is specifically for validator of api | ||
|
||
// export interface IZodValidator<T extends z.ZodRawShape, U extends z.ZodRawShape> { | ||
// query: z.ZodObject<T> | z.ZodUndefined, // T 用于表示 query 的 Zod schema 类型 | ||
// body: z.ZodObject<U> | z.ZodUndefined, // U 用于表示 body 的 Zod schema 类型 | ||
// } | ||
|
||
export interface IZodValidator< | ||
T extends z.ZodRawShape | z.ZodOptional<z.ZodNullable<z.ZodString>>, | ||
U extends z.ZodRawShape | z.ZodOptional<z.ZodNullable<z.ZodString>>, | ||
> { | ||
// Info: (20240909 - Murky) If T is undefined, query is z.ZodUndefined, otherwise it is z.ZodObject<T> | ||
query: T extends z.ZodRawShape ? z.ZodObject<T> : z.ZodOptional<z.ZodNullable<z.ZodString>>; | ||
|
||
// Info: (20240909 - Murky) If U is undefined, body is z.ZodUndefined, otherwise it is z.ZodObject<U> | ||
body: U extends z.ZodRawShape ? z.ZodObject<U> : z.ZodOptional<z.ZodNullable<z.ZodString>>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { API_ZOD_SCHEMA } from '@/constants/zod_schema'; | ||
import { NextApiRequest } from 'next'; | ||
import { z } from 'zod'; | ||
import { loggerRequest } from '@/lib/utils/logger_back'; | ||
import { APIPath } from '@/constants/api_connection'; | ||
/* | ||
* Info: (20240909 - Murky) Record need to implement all the keys of the enum, | ||
* it will cause error when not implement all the keys | ||
* use code below after all the keys are implemented | ||
*/ | ||
// import { APIName } from "@/constants/api_connection"; | ||
// export function validateRequest( | ||
// apiName: APIName, | ||
// req: NextApiRequest, | ||
// res: NextApiResponse) { | ||
type API_ZodSchema = typeof API_ZOD_SCHEMA; | ||
type QueryType<T extends keyof API_ZodSchema> = z.infer<API_ZodSchema[T]['query']>; | ||
type BodyType<T extends keyof API_ZodSchema> = z.infer<API_ZodSchema[T]['body']>; | ||
|
||
export function validateRequest<T extends keyof typeof API_ZOD_SCHEMA>( | ||
apiName: T, | ||
req: NextApiRequest, | ||
userId: number = -1 | ||
): { query: QueryType<T> | null; body: BodyType<T> | null } { | ||
const { query: queryValidator, body: bodyValidator } = API_ZOD_SCHEMA[apiName]; | ||
|
||
const { query, body } = req; | ||
|
||
// Info: (20240909 - Murky) Validate query and body | ||
const queryResult = queryValidator.safeParse(query); | ||
const bodyResult = bodyValidator.safeParse(body); | ||
|
||
// Info: (20240909 - Murky) If validation failed, it will return null, go to logger to check why it failed | ||
let payload: { | ||
query: QueryType<T> | null; | ||
body: BodyType<T> | null; | ||
} = { | ||
query: null, | ||
body: null, | ||
}; | ||
|
||
if (!queryResult.success || !bodyResult.success) { | ||
// Info: (20240909 - Murky) It will return why error is caused, or what is the data if success | ||
const errorFormat = { | ||
query: queryResult.error ? queryResult.error.format() : query.data, | ||
body: bodyResult.error ? bodyResult.error.format() : body.data, | ||
}; | ||
|
||
const logger = loggerRequest( | ||
userId, | ||
APIPath[apiName], | ||
req.method || 'unknown', | ||
400, | ||
errorFormat, | ||
req.headers['user-agent'] || 'unknown user-agent', | ||
req.socket.remoteAddress || 'unknown ip' | ||
); | ||
|
||
logger.error('Request validation failed'); | ||
} else { | ||
// Info: (20240909 - Murky) if validator is z.ZodOptional (which used when query or body is not needed), it will return null | ||
payload = { | ||
query: queryValidator instanceof z.ZodOptional ? null : queryResult.data, | ||
body: bodyValidator instanceof z.ZodOptional ? null : bodyResult.data, | ||
}; | ||
} | ||
|
||
return payload; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* Info: (20240909 - Murky) This file is to demonstrate how to use Zod schema to validate data. | ||
* check more validator function in:https://www.npmjs.com/package/zod#primitives | ||
*/ | ||
import { IZodValidator } from '@/interfaces/zod_validator'; | ||
import { z } from 'zod'; | ||
|
||
enum testEnum { | ||
A = 'A', | ||
B = 'B', | ||
C = 'C', | ||
} | ||
|
||
const queryValidator = z.object({ | ||
name: z.string().min(3), | ||
/** | ||
* Info: (20240909 - Murky) | ||
* There is plenty of ways to validate numeric string, | ||
* Check: https://github.com/colinhacks/zod/discussions/330 | ||
*/ | ||
age: z.string().regex(/^\d+$/).transform(Number), | ||
email: z.string().email(), | ||
password: z.string().min(6), | ||
testEnum: z.nativeEnum(testEnum), | ||
}); | ||
|
||
// Info: (20240909 - Murky) If you want to validate body, you can add bodyValidator | ||
// const bodyValidator = z.object({ | ||
// bodyName: z.string().min(3), | ||
// }); | ||
|
||
// export const zodExampleValidator: IZodValidator< | ||
// typeof queryValidator['shape'], | ||
// typeof bodyValidator['shape'] | ||
// > = { | ||
// query: queryValidator, | ||
// body: bodyValidator, | ||
// }; | ||
|
||
// Info: (20240909 - Murky) If you don't want to validate body, you can use z.string().nullish() | ||
const bodyValidator = z.string().nullish(); | ||
|
||
export const zodExampleValidator: IZodValidator< | ||
(typeof queryValidator)['shape'], | ||
typeof bodyValidator | ||
> = { | ||
query: queryValidator, | ||
body: bodyValidator, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* | ||
* Info: (20240909 - Murky) This api is to demonstrate how to use Zod schema to validate data. | ||
* Delete this file after all the keys are implemented | ||
*/ | ||
|
||
import { NextApiRequest, NextApiResponse } from 'next'; | ||
import { APIName } from '@/constants/api_connection'; | ||
import { validateRequest } from '@/lib/utils/request_validator'; | ||
|
||
export default async function handler(req: NextApiRequest, res: NextApiResponse) { | ||
const validatedQuery = validateRequest(APIName.ZOD_EXAMPLE, req); | ||
|
||
const { query, body } = validatedQuery; | ||
res.status(200).json({ query, body }); | ||
} |