Skip to content

Commit aade44c

Browse files
committed
refactor: add base global errors for zod
1 parent cdc90cd commit aade44c

File tree

10 files changed

+69
-42
lines changed

10 files changed

+69
-42
lines changed

app/app.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ import express from 'express'
22
import cors from 'cors'
33
import helmet from 'helmet'
44
import logger from 'morgan'
5+
import * as z from 'zod'
56

6-
import { env } from './config'
7+
import { env, zodConfig } from './config'
78
import { globalErrorHandler, notFoundHandler } from './middlewares'
89
import { apiRouter } from './routes'
910

1011
export const app = express()
1112

13+
z.config(zodConfig)
14+
1215
app.use(helmet())
1316
app.use(cors({ origin: env.ALLOWED_ORIGINS }))
1417
app.use(logger(app.get('env') === 'development' ? 'dev' : 'combined'))

app/config/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export { transport } from './mailer.config'
22
export { default } from './cloudinary.config'
33
export { env } from './env.config'
44
export { redisClient } from './redis.config'
5+
export { zodConfig } from './zod.config'
56
export { defaultUserAvatars } from './default-user-avatars.config'

app/config/zod.config.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { core } from 'zod'
2+
3+
export const zodConfig: core.$ZodConfig = {
4+
customError: ({
5+
input,
6+
code,
7+
minimum,
8+
maximum,
9+
expected,
10+
format,
11+
values
12+
}) => {
13+
if (!input) return 'The field is required'
14+
15+
if (code === 'too_small') {
16+
return `The field must be at least ${minimum} characters`
17+
}
18+
19+
if (code === 'too_big') {
20+
return `The field must be at most ${maximum} characters`
21+
}
22+
23+
if (code === 'invalid_value') {
24+
return `The field must be one of the following values: ${values.join(', ')}`
25+
}
26+
27+
if (code === 'invalid_format') {
28+
return `The field must have a valid ${format} format`
29+
}
30+
31+
if (code === 'invalid_type') return `The field must be of type ${expected}`
32+
33+
return undefined
34+
}
35+
}

app/schemas/auth.schema.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
11
import * as z from 'zod'
22

33
export const SigninSchema = z.object({
4-
email: z.email('Email is required'),
5-
password: z
6-
.string()
7-
.min(8, 'Password must be at least 8 characters')
8-
.max(64, 'Password must be at most 64 characters')
4+
email: z.email(),
5+
password: z.string().min(8).max(64)
96
})
107

118
export const SignupSchema = z.object({
129
...SigninSchema.shape,
13-
name: z.string().min(2, 'Name must be at least 2 characters')
10+
name: z.string().min(2)
1411
})
1512

1613
export const RefreshTokenSchema = z.object({
17-
refreshToken: z.string().min(1, 'Refresh token is required')
14+
refreshToken: z.string().min(1)
1815
})
1916

2017
export const GoogleCodeSchema = z.object({
21-
code: z.string().min(1, 'Code is required'),
18+
code: z.string().min(1),
2219
state: z.optional(z.string())
2320
})

app/schemas/board.schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const zObjectKeys = <T extends Record<string, unknown>>(obj: T) => {
1414
}
1515

1616
export const AddBoardSchema = z.object({
17-
title: z.string().min(3, 'Title must be at least 3 characters'),
17+
title: z.string().min(3),
1818
icon: z.enum(Icon),
1919
background: zObjectKeys(boardImages)
2020
})

app/schemas/card.schema.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ import * as z from 'zod'
44
import { objectIdSchema } from './object-id.schema'
55

66
export const AddCardSchema = z.object({
7-
title: z.string().min(3, 'Title must be at least 3 characters'),
8-
description: z.string().min(3, 'Description must be at least 3 characters'),
9-
priority: z.enum(
10-
Priority,
11-
`Priority must be one of the following: ${Object.values(Priority).join(',')}`
12-
),
13-
deadline: z.iso.datetime('Deadline must be a valid datetime')
7+
title: z.string().min(3),
8+
description: z.string().min(3),
9+
priority: z.enum(Priority),
10+
deadline: z.iso.datetime()
1411
})
1512

1613
export const EditCardSchema = z

app/schemas/column.schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as z from 'zod'
33
import { objectIdSchema } from './object-id.schema'
44

55
export const AddColumnSchema = z.object({
6-
title: z.string().min(3, 'Title must be at least 3 characters')
6+
title: z.string().min(3)
77
})
88

99
export const EditColumnSchema = AddColumnSchema.partial()

app/schemas/object-id.schema.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
import * as z from 'zod'
22

3-
export const objectIdSchema = () =>
4-
z.string().regex(/^[0-9a-f]{24}$/, 'Id must be a valid ObjectId')
3+
export const objectIdSchema = () => z.string().regex(/^[0-9a-f]{24}$/)

app/schemas/user.schema.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,15 @@ import { Theme } from '@prisma/client'
22
import * as z from 'zod'
33

44
export const NeedHelpSchema = z.object({
5-
email: z.email('Email is invalid'),
6-
comment: z.string().min(5, 'Comment must be at least 5 characters')
5+
email: z.email(),
6+
comment: z.string().min(5)
77
})
88

99
export const EditUserSchema = z
1010
.object({
11-
name: z.string().min(2, 'Name must be at least 2 characters'),
12-
email: z.email('Email is invalid'),
13-
password: z
14-
.string()
15-
.min(8, 'Password must be at least 8 characters')
16-
.max(64, 'Password must be at most 64 characters'),
17-
theme: z.enum(Theme, {
18-
message: `Theme must be one of the following: ${Object.values(Theme).join(',')}`
19-
})
11+
name: z.string().min(2),
12+
email: z.email(),
13+
password: z.string().min(8).max(64),
14+
theme: z.enum(Theme)
2015
})
2116
.partial()

swagger.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -982,9 +982,9 @@
982982
"example": {
983983
"statusCode": 400,
984984
"message": {
985-
"email": "Email is invalid",
986-
"password": "Password must be at least 8 characters",
987-
"name": "Name must be at least 2 characters"
985+
"email": "The field must have a valid email format",
986+
"password": "The field must be at least 8 characters",
987+
"name": "The field must be at least 2 characters"
988988
}
989989
}
990990
}
@@ -1091,7 +1091,7 @@
10911091
"example": {
10921092
"statusCode": 400,
10931093
"message": {
1094-
"ids": "Ids must be an array of valid ObjectIds"
1094+
"ids": "The field must be of type array"
10951095
}
10961096
}
10971097
}
@@ -1123,8 +1123,8 @@
11231123
"example": {
11241124
"statusCode": 400,
11251125
"message": {
1126-
"title": "Title must be at least 3 characters",
1127-
"icon": "Invalid enum value. Expected 'project' | 'star' | 'loading' | 'puzzle' | 'container' | 'lightning' | 'colors' | 'hexagon', received 'random-icon'"
1126+
"title": "The field must be at least 3 characters",
1127+
"icon": "The field must be one of the following values: project, star, loading, puzzle, container, lightning, colors, hexagon"
11281128
}
11291129
}
11301130
}
@@ -1179,8 +1179,8 @@
11791179
"example": {
11801180
"statusCode": 400,
11811181
"message": {
1182-
"title": "Title must be at least 3 characters",
1183-
"boardId": "Id must be a valid ObjectId"
1182+
"title": "The field must be at least 3 characters",
1183+
"boardId": "The field must have a valid regex format"
11841184
}
11851185
}
11861186
}
@@ -1212,9 +1212,9 @@
12121212
"example": {
12131213
"statusCode": 400,
12141214
"message": {
1215-
"description": "Title must be a string",
1216-
"priority": "Priority must be one of the following: Without, Low, Medium, High",
1217-
"columnId": "Id must be a valid ObjectId"
1215+
"description": "The field must be of type string",
1216+
"priority": "The field must be one of the following values: Without, Low, Medium, High",
1217+
"columnId": "The field must have a valid regex format"
12181218
}
12191219
}
12201220
}

0 commit comments

Comments
 (0)