prisma-generator-express 1.14.0 → 1.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -17
- package/dist/helpers/generateRouteFile.js +1 -1
- package/package.json +3 -3
- package/src/copy/transformZod.spec.ts +155 -106
- package/src/copy/transformZod.ts +21 -21
- package/src/helpers/generateRouteFile.ts +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ This tool helps you quickly create API endpoints in your Express app using your
|
|
|
10
10
|
|
|
11
11
|
When you run `npx prisma generate`, it automatically creates two things:
|
|
12
12
|
|
|
13
|
-
- Service functions that you can import into your Express routes. By default these functions handle CRUD operations and output validation. This behavior can be controlled.
|
|
13
|
+
- Service functions that you can import into your Express routes. By default, these functions handle CRUD operations and output validation. This behavior can be controlled.
|
|
14
14
|
- Router generator function that lets you select which routes to add to the application and which middlewares to apply.
|
|
15
15
|
|
|
16
16
|
## Table of Contents
|
|
@@ -19,7 +19,7 @@ When you run `npx prisma generate`, it automatically creates two things:
|
|
|
19
19
|
- [Basic Usage](#basic-usage)
|
|
20
20
|
- [Router Generator Usage](#router-generator-usage)
|
|
21
21
|
- [Request Object Properties](#request-object-properties)
|
|
22
|
-
- [
|
|
22
|
+
- [Router Schema](#router-schema)
|
|
23
23
|
|
|
24
24
|
# Installation
|
|
25
25
|
|
|
@@ -72,7 +72,7 @@ app.use((req, res, next) => {
|
|
|
72
72
|
- Here’s how you can use a generated function in your Express app:
|
|
73
73
|
|
|
74
74
|
```ts
|
|
75
|
-
import { UserFindUnique } from './generated/UserFindUnique' // Adjust the path as necessary
|
|
75
|
+
import { UserFindUnique } from './generated/api/UserFindUnique' // Adjust the path as necessary
|
|
76
76
|
import { FindUniqueUserSchema } from './prisma-zod-generator/schemas/FindUniqueUser.schema' // Adjust the path as necessary
|
|
77
77
|
import { FindUniqueUserSchemaOutput } from './prisma-zod-generator/schemas/FindUniqueUserOutput.schema' // Adjust the path as necessary
|
|
78
78
|
|
|
@@ -133,7 +133,7 @@ const addPrisma: RequestHandler = (
|
|
|
133
133
|
next: NextFunction,
|
|
134
134
|
) => {
|
|
135
135
|
req.prisma = prisma
|
|
136
|
-
// req.omitOutputValidation = true (not required if you use `select` instead of `include`)
|
|
136
|
+
// req.omitOutputValidation = true (output validation is not required if you use `select` instead of `include`)
|
|
137
137
|
next()
|
|
138
138
|
}
|
|
139
139
|
|
|
@@ -164,19 +164,19 @@ const afterFindFirst: RequestHandler = (
|
|
|
164
164
|
|
|
165
165
|
/**
|
|
166
166
|
* For generated route the middleware order will be as follows:
|
|
167
|
-
* 1. Query parser
|
|
167
|
+
* 1. Query parser (kicks in for GET requests)
|
|
168
168
|
* 2. Custom middlewares: config.{method}.before[]
|
|
169
|
-
* 3. Input validator middleware (Optional): config.{method}.input
|
|
169
|
+
* 3. Input validator middleware (Optional): config.{method}.input. For GET request validates `req.query`, for others - `req.body`
|
|
170
170
|
* 4. Generated middleware
|
|
171
171
|
* 5. Output validator middleware: config.{method}.input
|
|
172
172
|
* 6. Custom middlewares: config.{method}.after[] (not available if req.passToNext is falsy)
|
|
173
173
|
*/
|
|
174
|
-
const
|
|
174
|
+
const userAccounRouterConfig: RouteConfig<RequestHandler> = {
|
|
175
175
|
FindFirst: {
|
|
176
176
|
before: [beforeFindFirst],
|
|
177
177
|
after: [afterFindFirst],
|
|
178
178
|
input: {
|
|
179
|
-
schema: UserAccountFindFirstSchema,
|
|
179
|
+
schema: UserAccountFindFirstSchema, // make sure you set `isGenerateSelect = true` in prisma-zod-generator
|
|
180
180
|
allow: [
|
|
181
181
|
'select.id',
|
|
182
182
|
'select.full_name',
|
|
@@ -194,7 +194,7 @@ const someRouterConfig: RouteConfig<RequestHandler> = {
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
app.use(addPrisma)
|
|
197
|
-
app.use(UserAccountRouter(
|
|
197
|
+
app.use(UserAccountRouter(userAccounRouterConfig))
|
|
198
198
|
|
|
199
199
|
app.listen(3000, () => {
|
|
200
200
|
console.log('Server is running on http://localhost:3000')
|
|
@@ -208,9 +208,8 @@ The following properties can be attached to the `req` object to control the beha
|
|
|
208
208
|
| Property | Type | Description |
|
|
209
209
|
| ---------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
210
210
|
| `prisma` | PrismaClient | An instance of PrismaClient that allows the middleware to interact with your database. |
|
|
211
|
-
| `passToNext` | boolean | Optional, if `true` - the result of a Prisma request will be passed to
|
|
212
|
-
| `
|
|
213
|
-
| `outputValidation` | ZodTypeAny | (Optional) A Zod schema used to validate the data returned from the Prisma query before sending it to the client. This helps ensure the response adheres to expected data formats. |
|
|
211
|
+
| `passToNext` | boolean | Optional, if `true` - the result of a Prisma request will be passed to the next middleware as `if (req.locals) req.locals.data` |
|
|
212
|
+
| `outputValidation` | ZodTypeAny | (Optional) A Zod schema used to validate the data returned from the Prisma query before sending it to the client. |
|
|
214
213
|
| `omitOutputValidation` | Boolean | (Optional) A flag that, if set to `true`, disables output validation even if a Zod schema is provided. |
|
|
215
214
|
|
|
216
215
|
## Router Schema
|
|
@@ -219,7 +218,10 @@ The following properties can be attached to the `req` object to control the beha
|
|
|
219
218
|
| ------------ | -------- | ------------ |
|
|
220
219
|
| `findUnique` | `GET` | `/:id` |
|
|
221
220
|
| `findFirst` | `GET` | `/first` |
|
|
222
|
-
| `
|
|
221
|
+
| `findMany` | `GET` | `/` |
|
|
222
|
+
| `aggregate` | `GET` | `/aggregate` |
|
|
223
|
+
| `count` | `GET` | `/count` |
|
|
224
|
+
| `groupBy` | `GET` | `/groupby` |
|
|
223
225
|
| `create` | `POST` | `/` |
|
|
224
226
|
| `createMany` | `POST` | `/many` |
|
|
225
227
|
| `update` | `PUT` | `/` |
|
|
@@ -227,9 +229,7 @@ The following properties can be attached to the `req` object to control the beha
|
|
|
227
229
|
| `upsert` | `PATCH` | `/` |
|
|
228
230
|
| `delete` | `DELETE` | `/` |
|
|
229
231
|
| `deleteMany` | `DELETE` | `/many` |
|
|
230
|
-
|
|
231
|
-
| `count` | `GET` | `/count` |
|
|
232
|
-
| `groupBy` | `GET` | `/groupby` |
|
|
232
|
+
|
|
233
233
|
|
|
234
234
|
## Helper functions
|
|
235
235
|
|
|
@@ -248,7 +248,7 @@ interface ValidatorOptions {
|
|
|
248
248
|
|
|
249
249
|
### encodeQueryParams(params: Params)
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
It can be used on the frontend to encode Prisma-compatible queries. Alternatively `qs` can be used, but it probably won't work with `OR: [{ blah: false }, { blah: null }]` or some other edge cases.
|
|
252
252
|
|
|
253
253
|
```ts
|
|
254
254
|
type RecursiveUrlParams = {
|
|
@@ -27,7 +27,7 @@ import { ${modelName}GroupBy } from './${modelName}GroupBy';
|
|
|
27
27
|
import { createValidatorMiddleware, ValidatorOptions } from '../createValidatorMiddleware'
|
|
28
28
|
import { createOutputValidatorMiddleware } from '../createOutputValidatorMiddleware'
|
|
29
29
|
import { RouteConfig, ValidatorConfig } from '../routeConfig'
|
|
30
|
-
import { parseQueryParams } from "../
|
|
30
|
+
import { parseQueryParams } from "../parseQueryParams";
|
|
31
31
|
|
|
32
32
|
const defaultBeforeAfter = {
|
|
33
33
|
before: [] as RequestHandler[],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-generator-express",
|
|
3
3
|
"description": "Prisma generator of Express CRUD API",
|
|
4
|
-
"version": "1.14.
|
|
4
|
+
"version": "1.14.2",
|
|
5
5
|
"main": "dist/generator.js",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
"@types/express": "^4.17.21",
|
|
36
36
|
"@types/jest": "29.5.12",
|
|
37
37
|
"@types/lodash": "^4.17.4",
|
|
38
|
-
"@types/node": "20.12.
|
|
38
|
+
"@types/node": "20.12.13",
|
|
39
39
|
"@types/prettier": "3.0.0",
|
|
40
40
|
"jest": "29.7.0",
|
|
41
41
|
"prisma": "5.14.0",
|
|
42
42
|
"semantic-release": "^23.1.1",
|
|
43
|
-
"ts-jest": "29.1.
|
|
43
|
+
"ts-jest": "29.1.4",
|
|
44
44
|
"typescript": "5.4.5"
|
|
45
45
|
},
|
|
46
46
|
"homepage": "https://github.com/multipliedtwice/prisma-generator-express/blob/master/README.md",
|
|
@@ -553,106 +553,7 @@ describe('Cryptocurrency Schema Validation', () => {
|
|
|
553
553
|
expect(error).toBeInstanceOf(ZodError)
|
|
554
554
|
}
|
|
555
555
|
})
|
|
556
|
-
|
|
557
|
-
it('deep nesting', () => {
|
|
558
|
-
const taskSchema = z.object({
|
|
559
|
-
select: z
|
|
560
|
-
.object({
|
|
561
|
-
id: z.boolean().optional(),
|
|
562
|
-
project_id: z.boolean().optional(),
|
|
563
|
-
list_id: z.boolean().optional(),
|
|
564
|
-
user_assignments: z
|
|
565
|
-
.object({
|
|
566
|
-
select: z.object({
|
|
567
|
-
user: z.boolean().optional(),
|
|
568
|
-
}),
|
|
569
|
-
})
|
|
570
|
-
.optional(),
|
|
571
|
-
tags_mappings: z
|
|
572
|
-
.object({
|
|
573
|
-
select: z.object({
|
|
574
|
-
tag: z.boolean().optional(),
|
|
575
|
-
}),
|
|
576
|
-
})
|
|
577
|
-
.optional(),
|
|
578
|
-
attachments: z
|
|
579
|
-
.object({
|
|
580
|
-
select: z.object({
|
|
581
|
-
attachment: z.boolean().optional(),
|
|
582
|
-
}),
|
|
583
|
-
where: z
|
|
584
|
-
.object({
|
|
585
|
-
is_image: z.boolean().optional(),
|
|
586
|
-
})
|
|
587
|
-
.optional(),
|
|
588
|
-
take: z.number().optional(),
|
|
589
|
-
orderBy: z
|
|
590
|
-
.object({
|
|
591
|
-
created_at: z.enum(['asc', 'desc']).optional(),
|
|
592
|
-
})
|
|
593
|
-
.optional(),
|
|
594
|
-
})
|
|
595
|
-
.optional(),
|
|
596
|
-
rendered_description: z.boolean().optional(),
|
|
597
|
-
description: z.boolean().optional(),
|
|
598
|
-
created_at: z.boolean().optional(),
|
|
599
|
-
start_date: z.boolean().optional(),
|
|
600
|
-
reactions: z.boolean().optional(),
|
|
601
|
-
intervals: z.boolean().optional(),
|
|
602
|
-
column_id: z.boolean().optional(),
|
|
603
|
-
priority: z.boolean().optional(),
|
|
604
|
-
due_date: z.boolean().optional(),
|
|
605
|
-
column: z.boolean().optional(),
|
|
606
|
-
title: z.boolean().optional(),
|
|
607
|
-
order: z.boolean().optional(),
|
|
608
|
-
color: z.boolean().optional(),
|
|
609
|
-
})
|
|
610
|
-
.optional(),
|
|
611
|
-
where: z
|
|
612
|
-
.object({
|
|
613
|
-
id: z.string().optional(),
|
|
614
|
-
AND: z
|
|
615
|
-
.array(
|
|
616
|
-
z.object({
|
|
617
|
-
OR: z
|
|
618
|
-
.array(
|
|
619
|
-
z.object({
|
|
620
|
-
to_delete: z.boolean().nullable().optional(),
|
|
621
|
-
}),
|
|
622
|
-
)
|
|
623
|
-
.optional(),
|
|
624
|
-
}),
|
|
625
|
-
)
|
|
626
|
-
.optional(),
|
|
627
|
-
})
|
|
628
|
-
.optional(),
|
|
629
|
-
})
|
|
630
|
-
|
|
631
|
-
const allowedFields = [
|
|
632
|
-
'select.id',
|
|
633
|
-
'select.project_id',
|
|
634
|
-
'select.list_id',
|
|
635
|
-
'select.user_assignments.select.user',
|
|
636
|
-
'select.tags_mappings.select.tag',
|
|
637
|
-
'select.attachments.select.attachment',
|
|
638
|
-
'select.attachments.where.is_image',
|
|
639
|
-
'select.attachments.take',
|
|
640
|
-
'select.attachments.orderBy.created_at',
|
|
641
|
-
'select.rendered_description',
|
|
642
|
-
'select.description',
|
|
643
|
-
'select.created_at',
|
|
644
|
-
'select.start_date',
|
|
645
|
-
'select.reactions',
|
|
646
|
-
'select.intervals',
|
|
647
|
-
'select.column_id',
|
|
648
|
-
'select.priority',
|
|
649
|
-
'select.due_date',
|
|
650
|
-
'select.column',
|
|
651
|
-
'select.order',
|
|
652
|
-
'select.color',
|
|
653
|
-
'where.id',
|
|
654
|
-
'where.AND[].OR[].to_delete',
|
|
655
|
-
]
|
|
556
|
+
describe('Prisma example', () => {
|
|
656
557
|
const inputData = {
|
|
657
558
|
select: {
|
|
658
559
|
id: true,
|
|
@@ -703,12 +604,160 @@ describe('Cryptocurrency Schema Validation', () => {
|
|
|
703
604
|
],
|
|
704
605
|
},
|
|
705
606
|
}
|
|
607
|
+
it('deep nesting', () => {
|
|
608
|
+
const taskSchema = z.object({
|
|
609
|
+
select: z
|
|
610
|
+
.object({
|
|
611
|
+
id: z.boolean().optional(),
|
|
612
|
+
project_id: z.boolean().optional(),
|
|
613
|
+
list_id: z.boolean().optional(),
|
|
614
|
+
user_assignments: z
|
|
615
|
+
.object({
|
|
616
|
+
select: z.object({
|
|
617
|
+
user: z.boolean().optional(),
|
|
618
|
+
}),
|
|
619
|
+
})
|
|
620
|
+
.optional(),
|
|
621
|
+
tags_mappings: z
|
|
622
|
+
.object({
|
|
623
|
+
select: z.object({
|
|
624
|
+
tag: z.boolean().optional(),
|
|
625
|
+
}),
|
|
626
|
+
})
|
|
627
|
+
.optional(),
|
|
628
|
+
attachments: z
|
|
629
|
+
.object({
|
|
630
|
+
select: z.object({
|
|
631
|
+
attachment: z.boolean().optional(),
|
|
632
|
+
}),
|
|
633
|
+
where: z
|
|
634
|
+
.object({
|
|
635
|
+
is_image: z.boolean().optional(),
|
|
636
|
+
})
|
|
637
|
+
.optional(),
|
|
638
|
+
take: z.number().optional(),
|
|
639
|
+
orderBy: z
|
|
640
|
+
.object({
|
|
641
|
+
created_at: z.enum(['asc', 'desc']).optional(),
|
|
642
|
+
})
|
|
643
|
+
.optional(),
|
|
644
|
+
})
|
|
645
|
+
.optional(),
|
|
646
|
+
rendered_description: z.boolean().optional(),
|
|
647
|
+
description: z.boolean().optional(),
|
|
648
|
+
created_at: z.boolean().optional(),
|
|
649
|
+
start_date: z.boolean().optional(),
|
|
650
|
+
reactions: z.boolean().optional(),
|
|
651
|
+
intervals: z.boolean().optional(),
|
|
652
|
+
column_id: z.boolean().optional(),
|
|
653
|
+
priority: z.boolean().optional(),
|
|
654
|
+
due_date: z.boolean().optional(),
|
|
655
|
+
column: z.boolean().optional(),
|
|
656
|
+
title: z.boolean().optional(),
|
|
657
|
+
order: z.boolean().optional(),
|
|
658
|
+
color: z.boolean().optional(),
|
|
659
|
+
})
|
|
660
|
+
.optional(),
|
|
661
|
+
where: z
|
|
662
|
+
.object({
|
|
663
|
+
id: z.string().optional(),
|
|
664
|
+
AND: z
|
|
665
|
+
.array(
|
|
666
|
+
z.object({
|
|
667
|
+
OR: z
|
|
668
|
+
.array(
|
|
669
|
+
z.object({
|
|
670
|
+
to_delete: z.boolean().nullable().optional(),
|
|
671
|
+
}),
|
|
672
|
+
)
|
|
673
|
+
.optional(),
|
|
674
|
+
}),
|
|
675
|
+
)
|
|
676
|
+
.optional(),
|
|
677
|
+
})
|
|
678
|
+
.optional(),
|
|
679
|
+
})
|
|
706
680
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
681
|
+
const allowedFields = [
|
|
682
|
+
'select.id',
|
|
683
|
+
'select.project_id',
|
|
684
|
+
'select.list_id',
|
|
685
|
+
'select.user_assignments.select.user',
|
|
686
|
+
'select.tags_mappings.select.tag',
|
|
687
|
+
'select.attachments.select.attachment',
|
|
688
|
+
'select.attachments.where.is_image',
|
|
689
|
+
'select.attachments.take',
|
|
690
|
+
'select.attachments.orderBy.created_at',
|
|
691
|
+
'select.rendered_description',
|
|
692
|
+
'select.description',
|
|
693
|
+
'select.created_at',
|
|
694
|
+
'select.start_date',
|
|
695
|
+
'select.reactions',
|
|
696
|
+
'select.intervals',
|
|
697
|
+
'select.column_id',
|
|
698
|
+
'select.priority',
|
|
699
|
+
'select.due_date',
|
|
700
|
+
'select.column',
|
|
701
|
+
'select.order',
|
|
702
|
+
'select.color',
|
|
703
|
+
'where.id',
|
|
704
|
+
'where.AND[].OR[].to_delete',
|
|
705
|
+
]
|
|
706
|
+
|
|
707
|
+
try {
|
|
708
|
+
const result = allow(taskSchema, allowedFields).safeParse(inputData)
|
|
709
|
+
expect(result.success).toBe(false)
|
|
710
|
+
} catch (error) {
|
|
711
|
+
expect(error).toBeInstanceOf(ZodError)
|
|
712
|
+
}
|
|
713
|
+
})
|
|
714
|
+
|
|
715
|
+
it('deep nesting 2', () => {
|
|
716
|
+
const taskSchema = z.object({
|
|
717
|
+
where: z
|
|
718
|
+
.object({
|
|
719
|
+
id: z.string().optional(),
|
|
720
|
+
AND: z
|
|
721
|
+
.array(
|
|
722
|
+
z.object({
|
|
723
|
+
OR: z
|
|
724
|
+
.array(
|
|
725
|
+
z.object({
|
|
726
|
+
to_delete: z.boolean().nullable().optional(),
|
|
727
|
+
}),
|
|
728
|
+
)
|
|
729
|
+
.optional(),
|
|
730
|
+
}),
|
|
731
|
+
)
|
|
732
|
+
.optional(),
|
|
733
|
+
})
|
|
734
|
+
.optional(),
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
const allowedFields = [
|
|
738
|
+
'select.id',
|
|
739
|
+
'select.project_id',
|
|
740
|
+
'select.list_id',
|
|
741
|
+
'select.user_assignments.select.user',
|
|
742
|
+
'select.tags_mappings.select.tag',
|
|
743
|
+
'select.attachments.select.attachment',
|
|
744
|
+
'select.attachments.where.is_image',
|
|
745
|
+
'select.attachments.take',
|
|
746
|
+
'select.attachments.orderBy.created_at',
|
|
747
|
+
'select.rendered_description',
|
|
748
|
+
'select.description',
|
|
749
|
+
'select.created_at',
|
|
750
|
+
'select.start_date',
|
|
751
|
+
'where.id',
|
|
752
|
+
'where.AND[].OR[].to_delete',
|
|
753
|
+
]
|
|
754
|
+
|
|
755
|
+
try {
|
|
756
|
+
const result = allow(taskSchema, allowedFields).safeParse(inputData)
|
|
757
|
+
expect(result.success).toBe(false)
|
|
758
|
+
} catch (error) {
|
|
759
|
+
expect(error).toBeInstanceOf(ZodError)
|
|
760
|
+
}
|
|
761
|
+
})
|
|
713
762
|
})
|
|
714
763
|
})
|
package/src/copy/transformZod.ts
CHANGED
|
@@ -38,10 +38,11 @@ export function allow<T extends ZodTypeAny>(
|
|
|
38
38
|
schema: T,
|
|
39
39
|
allowedPaths: string[],
|
|
40
40
|
): ZodEffects<T, any, any> {
|
|
41
|
-
const rootSchema = schema instanceof z.ZodObject ? schema : undefined
|
|
41
|
+
const rootSchema = schema instanceof z.ZodObject ? schema.strict() : undefined
|
|
42
42
|
|
|
43
|
-
return
|
|
43
|
+
return rootSchema?.transform((data) => {
|
|
44
44
|
const flatData = flattenObject(data, '', rootSchema)
|
|
45
|
+
|
|
45
46
|
const disallowedPaths: string[] = []
|
|
46
47
|
|
|
47
48
|
for (const key of Object.keys(flatData)) {
|
|
@@ -51,19 +52,11 @@ export function allow<T extends ZodTypeAny>(
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
if (disallowedPaths.length > 0) {
|
|
54
|
-
|
|
55
|
-
for (const path of disallowedPaths) {
|
|
56
|
-
errors.push({
|
|
57
|
-
code: ZodIssueCode.custom,
|
|
58
|
-
message: `Field '${path}' is not allowed.`,
|
|
59
|
-
path: path.split('.'),
|
|
60
|
-
})
|
|
61
|
-
}
|
|
62
|
-
throw new ZodError(errors)
|
|
55
|
+
throw createZodErrorFromPaths(disallowedPaths, 'Field is not allowed:')
|
|
63
56
|
}
|
|
64
57
|
|
|
65
58
|
return data
|
|
66
|
-
}) as ZodEffects<T, any, any>
|
|
59
|
+
}) as unknown as ZodEffects<T, any, any>
|
|
67
60
|
}
|
|
68
61
|
|
|
69
62
|
export function forbid<T extends z.ZodTypeAny>(
|
|
@@ -82,15 +75,7 @@ export function forbid<T extends z.ZodTypeAny>(
|
|
|
82
75
|
}
|
|
83
76
|
|
|
84
77
|
if (forbiddenMatches.length > 0) {
|
|
85
|
-
|
|
86
|
-
for (const path of forbiddenMatches) {
|
|
87
|
-
errors.push({
|
|
88
|
-
code: ZodIssueCode.custom,
|
|
89
|
-
message: `Field '${path}' is forbidden.`,
|
|
90
|
-
path: [path],
|
|
91
|
-
})
|
|
92
|
-
}
|
|
93
|
-
throw new ZodError(errors)
|
|
78
|
+
throw createZodErrorFromPaths(forbiddenMatches, 'Field is forbidden:')
|
|
94
79
|
}
|
|
95
80
|
return data
|
|
96
81
|
}) as ZodEffects<T, any, any>
|
|
@@ -138,3 +123,18 @@ export function flattenObject(
|
|
|
138
123
|
flatten(obj, prefix, schema)
|
|
139
124
|
return result
|
|
140
125
|
}
|
|
126
|
+
|
|
127
|
+
function createZodErrorFromPaths(
|
|
128
|
+
disallowedPaths: string[],
|
|
129
|
+
errorMessage: string,
|
|
130
|
+
): ZodError {
|
|
131
|
+
const errors: ZodIssue[] = []
|
|
132
|
+
for (const path of disallowedPaths) {
|
|
133
|
+
errors.push({
|
|
134
|
+
code: ZodIssueCode.custom,
|
|
135
|
+
message: `${errorMessage} '${path}'`,
|
|
136
|
+
path: path.split('.'),
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
return new ZodError(errors)
|
|
140
|
+
}
|
|
@@ -31,7 +31,7 @@ import { ${modelName}GroupBy } from './${modelName}GroupBy';
|
|
|
31
31
|
import { createValidatorMiddleware, ValidatorOptions } from '../createValidatorMiddleware'
|
|
32
32
|
import { createOutputValidatorMiddleware } from '../createOutputValidatorMiddleware'
|
|
33
33
|
import { RouteConfig, ValidatorConfig } from '../routeConfig'
|
|
34
|
-
import { parseQueryParams } from "../
|
|
34
|
+
import { parseQueryParams } from "../parseQueryParams";
|
|
35
35
|
|
|
36
36
|
const defaultBeforeAfter = {
|
|
37
37
|
before: [] as RequestHandler[],
|