prisma-generator-express 1.12.0 → 1.14.0
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 +87 -17
- package/dist/generator.js +2 -12
- package/dist/generator.js.map +1 -1
- package/dist/helpers/generateAggregate.js +13 -13
- package/dist/helpers/generateAggregate.js.map +1 -1
- package/dist/helpers/generateCount.js +12 -13
- package/dist/helpers/generateCount.js.map +1 -1
- package/dist/helpers/generateCreate.js +13 -15
- package/dist/helpers/generateCreate.js.map +1 -1
- package/dist/helpers/generateCreateMany.js +13 -15
- package/dist/helpers/generateCreateMany.js.map +1 -1
- package/dist/helpers/generateDelete.js +12 -15
- package/dist/helpers/generateDelete.js.map +1 -1
- package/dist/helpers/generateDeleteMany.js +13 -14
- package/dist/helpers/generateDeleteMany.js.map +1 -1
- package/dist/helpers/generateFindFirst.js +10 -15
- package/dist/helpers/generateFindFirst.js.map +1 -1
- package/dist/helpers/generateFindMany.js +10 -15
- package/dist/helpers/generateFindMany.js.map +1 -1
- package/dist/helpers/generateFindUnique.js +10 -15
- package/dist/helpers/generateFindUnique.js.map +1 -1
- package/dist/helpers/generateGroupBy.js +12 -13
- package/dist/helpers/generateGroupBy.js.map +1 -1
- package/dist/helpers/generateRouteFile.js +68 -35
- package/dist/helpers/generateRouteFile.js.map +1 -1
- package/dist/helpers/generateUpdate.js +12 -15
- package/dist/helpers/generateUpdate.js.map +1 -1
- package/dist/helpers/generateUpdateMany.js +12 -15
- package/dist/helpers/generateUpdateMany.js.map +1 -1
- package/dist/helpers/generateUpsert.js +12 -15
- package/dist/helpers/generateUpsert.js.map +1 -1
- package/dist/utils/copyFiles.js +26 -0
- package/dist/utils/copyFiles.js.map +1 -0
- package/package.json +4 -1
- package/src/copy/createOutputValidatorMiddleware.ts +44 -0
- package/src/copy/createValidatorMiddleware.ts +55 -0
- package/src/copy/encodeQueryParams.spec.ts +303 -0
- package/src/copy/encodeQueryParams.ts +44 -0
- package/src/copy/misc.spec.ts +62 -0
- package/src/copy/misc.ts +25 -0
- package/src/copy/parseQueryParams.spec.ts +187 -0
- package/src/copy/parseQueryParams.ts +42 -0
- package/src/copy/routeConfig.ts +34 -0
- package/src/copy/transformZod.spec.ts +714 -0
- package/src/copy/transformZod.ts +140 -0
- package/src/generator.ts +3 -13
- package/src/helpers/generateAggregate.ts +13 -13
- package/src/helpers/generateCount.ts +12 -13
- package/src/helpers/generateCreate.ts +14 -15
- package/src/helpers/generateCreateMany.ts +13 -15
- package/src/helpers/generateDelete.ts +13 -15
- package/src/helpers/generateDeleteMany.ts +14 -14
- package/src/helpers/generateFindFirst.ts +10 -15
- package/src/helpers/generateFindMany.ts +10 -15
- package/src/helpers/generateFindUnique.ts +10 -15
- package/src/helpers/generateGroupBy.ts +12 -13
- package/src/helpers/generateRouteFile.ts +68 -35
- package/src/helpers/generateUpdate.ts +13 -15
- package/src/helpers/generateUpdateMany.ts +13 -15
- package/src/helpers/generateUpsert.ts +13 -15
- package/src/utils/copyFiles.ts +27 -0
- package/dist/helpers/generateQsParser.js +0 -56
- package/dist/helpers/generateQsParser.js.map +0 -1
- package/dist/helpers/generateRouteConfigType.js +0 -34
- package/dist/helpers/generateRouteConfigType.js.map +0 -1
- package/src/helpers/generateQsParser.ts +0 -51
- package/src/helpers/generateRouteConfigType.ts +0 -29
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { get } from 'lodash'
|
|
2
|
+
import {
|
|
3
|
+
z,
|
|
4
|
+
ZodEffects,
|
|
5
|
+
ZodError,
|
|
6
|
+
ZodIssue,
|
|
7
|
+
ZodIssueCode,
|
|
8
|
+
ZodObject,
|
|
9
|
+
ZodTypeAny,
|
|
10
|
+
} from 'zod'
|
|
11
|
+
|
|
12
|
+
function startsWith(str: string, prefix: string): boolean {
|
|
13
|
+
return str.slice(0, prefix.length) === prefix
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function every<T>(
|
|
17
|
+
array: T[],
|
|
18
|
+
callback: (value: T, index: number, array: T[]) => boolean,
|
|
19
|
+
): boolean {
|
|
20
|
+
for (let i = 0; i < array.length; i++) {
|
|
21
|
+
if (!callback(array[i], i, array)) {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isKeyAllowed(key: string, allowedPaths: string[]): boolean {
|
|
29
|
+
return !every(
|
|
30
|
+
allowedPaths,
|
|
31
|
+
(path) =>
|
|
32
|
+
!startsWith(key.replace(/\[\d+\]/g, ''), path.replace(/\[\d+\]/g, '')) &&
|
|
33
|
+
!startsWith(path.replace(/\[\d+\]/g, ''), key.replace(/\[\d+\]/g, '')),
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function allow<T extends ZodTypeAny>(
|
|
38
|
+
schema: T,
|
|
39
|
+
allowedPaths: string[],
|
|
40
|
+
): ZodEffects<T, any, any> {
|
|
41
|
+
const rootSchema = schema instanceof z.ZodObject ? schema : undefined
|
|
42
|
+
|
|
43
|
+
return schema.transform((data) => {
|
|
44
|
+
const flatData = flattenObject(data, '', rootSchema)
|
|
45
|
+
const disallowedPaths: string[] = []
|
|
46
|
+
|
|
47
|
+
for (const key of Object.keys(flatData)) {
|
|
48
|
+
if (!isKeyAllowed(key, allowedPaths)) {
|
|
49
|
+
disallowedPaths.push(key)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (disallowedPaths.length > 0) {
|
|
54
|
+
const errors: ZodIssue[] = []
|
|
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)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return data
|
|
66
|
+
}) as ZodEffects<T, any, any>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function forbid<T extends z.ZodTypeAny>(
|
|
70
|
+
schema: T,
|
|
71
|
+
forbiddenPaths: string[],
|
|
72
|
+
): ZodEffects<T, any, any> {
|
|
73
|
+
return schema.transform((data) => {
|
|
74
|
+
const forbiddenMatches: string[] = []
|
|
75
|
+
|
|
76
|
+
for (const forbiddenPath of forbiddenPaths) {
|
|
77
|
+
const value = get(data, forbiddenPath)
|
|
78
|
+
|
|
79
|
+
if (value !== undefined) {
|
|
80
|
+
forbiddenMatches.push(forbiddenPath)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (forbiddenMatches.length > 0) {
|
|
85
|
+
const errors: ZodIssue[] = []
|
|
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)
|
|
94
|
+
}
|
|
95
|
+
return data
|
|
96
|
+
}) as ZodEffects<T, any, any>
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function flattenObject(
|
|
100
|
+
obj: Record<string, any>,
|
|
101
|
+
prefix = '',
|
|
102
|
+
schema?: ZodObject<any>,
|
|
103
|
+
): Record<string, any> {
|
|
104
|
+
const result: Record<string, any> = {}
|
|
105
|
+
|
|
106
|
+
function flatten(current: any, prop: string, schema?: ZodObject<any>) {
|
|
107
|
+
if (Object(current) !== current) {
|
|
108
|
+
result[prop] = current
|
|
109
|
+
} else if (Array.isArray(current)) {
|
|
110
|
+
current.forEach((item, index) => {
|
|
111
|
+
flatten(item, `${prop}[]`, schema)
|
|
112
|
+
})
|
|
113
|
+
} else {
|
|
114
|
+
let isEmpty = true
|
|
115
|
+
for (const key in current) {
|
|
116
|
+
if (current.hasOwnProperty(key)) {
|
|
117
|
+
isEmpty = false
|
|
118
|
+
const currentSchema = schema?.shape[key]
|
|
119
|
+
if (
|
|
120
|
+
currentSchema instanceof z.ZodOptional &&
|
|
121
|
+
current[key] === undefined
|
|
122
|
+
) {
|
|
123
|
+
continue
|
|
124
|
+
}
|
|
125
|
+
flatten(
|
|
126
|
+
current[key],
|
|
127
|
+
prop ? `${prop}.${key}` : key,
|
|
128
|
+
currentSchema instanceof ZodObject ? currentSchema : undefined,
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (isEmpty) {
|
|
133
|
+
result[prop] = {}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
flatten(obj, prefix, schema)
|
|
139
|
+
return result
|
|
140
|
+
}
|
package/src/generator.ts
CHANGED
|
@@ -17,8 +17,8 @@ import { generateDeleteManyFunction } from './helpers/generateDeleteMany'
|
|
|
17
17
|
import { generateAggregateFunction } from './helpers/generateAggregate'
|
|
18
18
|
import { generateCountFunction } from './helpers/generateCount'
|
|
19
19
|
import { generateGroupByFunction } from './helpers/generateGroupBy'
|
|
20
|
-
|
|
21
|
-
import {
|
|
20
|
+
|
|
21
|
+
import { copyFiles } from './utils/copyFiles'
|
|
22
22
|
|
|
23
23
|
const { version } = require('../package.json')
|
|
24
24
|
|
|
@@ -173,16 +173,6 @@ generatorHandler({
|
|
|
173
173
|
})
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
await
|
|
177
|
-
content: generateRouteConfigType(),
|
|
178
|
-
options,
|
|
179
|
-
operation: 'RouteConfig',
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
await writeFileSafely({
|
|
183
|
-
content: generateParseQueryParams(),
|
|
184
|
-
options,
|
|
185
|
-
operation: 'ParseQueryParams',
|
|
186
|
-
})
|
|
176
|
+
await copyFiles(options)
|
|
187
177
|
},
|
|
188
178
|
})
|
|
@@ -23,42 +23,42 @@ import { Request, Response, NextFunction } from 'express';
|
|
|
23
23
|
import { RequestHandler, ParamsDictionary } from 'express-serve-static-core'
|
|
24
24
|
import { ParsedQs } from 'qs'
|
|
25
25
|
import { ZodTypeAny } from 'zod';
|
|
26
|
+
import { ValidatorConfig } from '../routeConfig'
|
|
26
27
|
|
|
27
28
|
interface AggregateRequest extends Request {
|
|
28
29
|
prisma: PrismaClient;
|
|
29
30
|
query: Partial<${argsTypeName}> & ParsedQs;
|
|
30
31
|
outputValidation?: ZodTypeAny;
|
|
31
32
|
omitOutputValidation?: boolean;
|
|
33
|
+
locals?: {
|
|
34
|
+
outputValidator?: ValidatorConfig;
|
|
35
|
+
};
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
export type AggregateMiddleware = RequestHandler<ParamsDictionary, any, Partial<${argsTypeName}>, Record<string, any>>;
|
|
35
39
|
|
|
36
40
|
export async function ${functionName}(req: AggregateRequest, res: Response, next: NextFunction) {
|
|
37
41
|
try {
|
|
38
|
-
|
|
42
|
+
const outputValidator = res.locals.outputValidator?.schema || req.outputValidation;
|
|
43
|
+
|
|
44
|
+
if (!outputValidator && !req.omitOutputValidation) {
|
|
39
45
|
throw new Error('Output validation schema or omission flag must be provided.');
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
const result = await req.prisma.${lowercaseFirstLetter(modelName)}.aggregate(req.query as ${argsTypeName});
|
|
43
49
|
|
|
44
|
-
if (!req.omitOutputValidation &&
|
|
45
|
-
const validationResult =
|
|
50
|
+
if (!req.omitOutputValidation && outputValidator) {
|
|
51
|
+
const validationResult = outputValidator.safeParse(result);
|
|
46
52
|
if (validationResult.success) {
|
|
47
|
-
res.status(200).json(validationResult.data);
|
|
53
|
+
return res.status(200).json(validationResult.data);
|
|
48
54
|
} else {
|
|
49
|
-
res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
55
|
+
return res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
50
56
|
}
|
|
51
57
|
} else {
|
|
52
|
-
res.status(200).json(result);
|
|
58
|
+
return res.status(200).json(result);
|
|
53
59
|
}
|
|
54
60
|
} catch (error: unknown) {
|
|
55
|
-
|
|
56
|
-
if (error instanceof Error) {
|
|
57
|
-
res.status(500).json({ error: error.message });
|
|
58
|
-
} else {
|
|
59
|
-
res.status(500).json({ error: "Unknown error occurred" });
|
|
60
|
-
}
|
|
61
|
-
next(error);
|
|
61
|
+
return next(error);
|
|
62
62
|
}
|
|
63
63
|
}`
|
|
64
64
|
}
|
|
@@ -29,36 +29,35 @@ interface CountRequest extends Request {
|
|
|
29
29
|
query: Partial<${argsTypeName}> & ParsedQs;
|
|
30
30
|
outputValidation?: ZodTypeAny;
|
|
31
31
|
omitOutputValidation?: boolean;
|
|
32
|
+
locals?: {
|
|
33
|
+
outputValidator?: ZodTypeAny;
|
|
34
|
+
};
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
export type CountMiddleware = RequestHandler<ParamsDictionary, any, {}, ParsedQs>;
|
|
35
38
|
|
|
36
39
|
export async function ${functionName}(req: CountRequest, res: Response, next: NextFunction) {
|
|
37
40
|
try {
|
|
38
|
-
|
|
41
|
+
const outputValidator = req.locals?.outputValidator || req.outputValidation;
|
|
42
|
+
|
|
43
|
+
if (!outputValidator && !req.omitOutputValidation) {
|
|
39
44
|
throw new Error('Output validation schema or omission flag must be provided.');
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
const result = await req.prisma.${lowercaseFirstLetter(modelName)}.count(req.query as ${argsTypeName});
|
|
43
48
|
|
|
44
|
-
if (!req.omitOutputValidation &&
|
|
45
|
-
const validationResult =
|
|
49
|
+
if (!req.omitOutputValidation && outputValidator) {
|
|
50
|
+
const validationResult = outputValidator.safeParse(result);
|
|
46
51
|
if (validationResult.success) {
|
|
47
|
-
res.status(200).json(validationResult.data);
|
|
52
|
+
return res.status(200).json(validationResult.data);
|
|
48
53
|
} else {
|
|
49
|
-
res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
54
|
+
return res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
50
55
|
}
|
|
51
56
|
} else {
|
|
52
|
-
res.status(200).json(result);
|
|
57
|
+
return res.status(200).json(result);
|
|
53
58
|
}
|
|
54
59
|
} catch (error: unknown) {
|
|
55
|
-
|
|
56
|
-
if (error instanceof Error) {
|
|
57
|
-
res.status(500).json({ error: error.message });
|
|
58
|
-
} else {
|
|
59
|
-
res.status(500).json({ error: "Unknown error occurred" });
|
|
60
|
-
}
|
|
61
|
-
next(error);
|
|
60
|
+
return next(error);
|
|
62
61
|
}
|
|
63
62
|
}`
|
|
64
63
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DMMF } from '@prisma/generator-helper'
|
|
2
2
|
import { lowercaseFirstLetter } from '../utils/strings'
|
|
3
|
+
|
|
3
4
|
/**
|
|
4
5
|
* Generates an Express middleware function that handles creation of records and includes conditional output validation with Zod.
|
|
5
6
|
* This version dynamically includes the correct type for the arguments based on the Prisma model.
|
|
@@ -26,37 +27,35 @@ interface CreateRequest extends Request {
|
|
|
26
27
|
body: ${argsTypeName};
|
|
27
28
|
outputValidation?: ZodTypeAny;
|
|
28
29
|
omitOutputValidation?: boolean;
|
|
30
|
+
locals?: {
|
|
31
|
+
outputValidator?: ZodTypeAny;
|
|
32
|
+
};
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
export type CreateMiddleware = RequestHandler<ParamsDictionary, any, ${argsTypeName}, Record<string, any>>
|
|
32
36
|
|
|
33
37
|
export async function ${functionName}(req: CreateRequest, res: Response, next: NextFunction) {
|
|
34
38
|
try {
|
|
35
|
-
|
|
39
|
+
const outputValidator = req.locals?.outputValidator || req.outputValidation;
|
|
40
|
+
|
|
41
|
+
if (!outputValidator && !req.omitOutputValidation) {
|
|
36
42
|
throw new Error('Output validation schema or omission flag must be provided.');
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
const data = await req.prisma.${lowercaseFirstLetter(modelName)}.create(req.body);
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
|
|
47
|
+
if (!req.omitOutputValidation && outputValidator) {
|
|
48
|
+
const validationResult = outputValidator.safeParse(data);
|
|
42
49
|
if (validationResult.success) {
|
|
43
|
-
res.status(201).json(validationResult.data);
|
|
50
|
+
return res.status(201).json(validationResult.data);
|
|
44
51
|
} else {
|
|
45
|
-
res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
52
|
+
return res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
46
53
|
}
|
|
47
|
-
} else if (!req.omitOutputValidation) {
|
|
48
|
-
throw new Error('Output validation schema must be provided unless explicitly omitted.');
|
|
49
54
|
} else {
|
|
50
|
-
res.status(201).json(data);
|
|
55
|
+
return res.status(201).json(data);
|
|
51
56
|
}
|
|
52
57
|
} catch (error: unknown) {
|
|
53
|
-
|
|
54
|
-
if (error instanceof Error) {
|
|
55
|
-
res.status(500).json({ error: error.message });
|
|
56
|
-
} else {
|
|
57
|
-
res.status(500).json({ error: "Unknown error occurred" });
|
|
58
|
-
}
|
|
59
|
-
next(error);
|
|
58
|
+
return next(error);
|
|
60
59
|
}
|
|
61
60
|
}`
|
|
62
61
|
}
|
|
@@ -26,37 +26,35 @@ interface CreateManyRequest extends Request {
|
|
|
26
26
|
body: ${argsTypeName};
|
|
27
27
|
outputValidation?: ZodTypeAny;
|
|
28
28
|
omitOutputValidation?: boolean;
|
|
29
|
+
locals?: {
|
|
30
|
+
outputValidator?: ZodTypeAny;
|
|
31
|
+
};
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
export type CreateManyMiddleware = RequestHandler<ParamsDictionary, any, ${argsTypeName}, Record<string, any>>
|
|
32
35
|
|
|
33
36
|
export async function ${functionName}(req: CreateManyRequest, res: Response, next: NextFunction) {
|
|
34
37
|
try {
|
|
35
|
-
|
|
38
|
+
const outputValidator = req.locals?.outputValidator || req.outputValidation;
|
|
39
|
+
|
|
40
|
+
if (!outputValidator && !req.omitOutputValidation) {
|
|
36
41
|
throw new Error('Output validation schema or omission flag must be provided.');
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
const data = await req.prisma.${lowercaseFirstLetter(modelName)}.createMany(req.body);
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
|
|
46
|
+
if (!req.omitOutputValidation && outputValidator) {
|
|
47
|
+
const validationResult = outputValidator.safeParse(data);
|
|
42
48
|
if (validationResult.success) {
|
|
43
|
-
res.status(201).json(validationResult.data);
|
|
49
|
+
return res.status(201).json(validationResult.data);
|
|
44
50
|
} else {
|
|
45
|
-
res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
51
|
+
return res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
46
52
|
}
|
|
47
|
-
} else if (!req.omitOutputValidation) {
|
|
48
|
-
throw new Error('Output validation schema must be provided unless explicitly omitted.');
|
|
49
53
|
} else {
|
|
50
|
-
res.status(201).json(data);
|
|
54
|
+
return res.status(201).json(data);
|
|
51
55
|
}
|
|
52
56
|
} catch (error: unknown) {
|
|
53
|
-
|
|
54
|
-
if (error instanceof Error) {
|
|
55
|
-
res.status(500).json({ error: error.message });
|
|
56
|
-
} else {
|
|
57
|
-
res.status(500).json({ error: "Unknown error occurred" });
|
|
58
|
-
}
|
|
59
|
-
next(error);
|
|
57
|
+
return next(error);
|
|
60
58
|
}
|
|
61
59
|
}`
|
|
62
60
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DMMF } from '@prisma/generator-helper'
|
|
2
2
|
import { lowercaseFirstLetter } from '../utils/strings'
|
|
3
|
+
|
|
3
4
|
/**
|
|
4
5
|
* Generates an Express middleware function that handles deleting records
|
|
5
6
|
* and includes conditional output validation with Zod.
|
|
@@ -27,38 +28,35 @@ interface DeleteRequest extends Request {
|
|
|
27
28
|
body: ${argsTypeName};
|
|
28
29
|
outputValidation?: ZodTypeAny;
|
|
29
30
|
omitOutputValidation?: boolean;
|
|
31
|
+
locals?: {
|
|
32
|
+
outputValidator?: ZodTypeAny;
|
|
33
|
+
};
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
export type DeleteMiddleware = RequestHandler<ParamsDictionary, any, ${argsTypeName}, Record<string, any>>
|
|
33
37
|
|
|
34
38
|
export async function ${functionName}(req: DeleteRequest, res: Response, next: NextFunction) {
|
|
35
39
|
try {
|
|
36
|
-
|
|
40
|
+
const outputValidator = req.locals?.outputValidator || req.outputValidation;
|
|
41
|
+
|
|
42
|
+
if (!outputValidator && !req.omitOutputValidation) {
|
|
37
43
|
throw new Error('Output validation schema or omission flag must be provided.');
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
const data = await req.prisma.${lowercaseFirstLetter(modelName)}.delete(req.body);
|
|
41
47
|
|
|
42
|
-
if (!req.omitOutputValidation &&
|
|
43
|
-
const validationResult =
|
|
48
|
+
if (!req.omitOutputValidation && outputValidator) {
|
|
49
|
+
const validationResult = outputValidator.safeParse(data);
|
|
44
50
|
if (validationResult.success) {
|
|
45
|
-
res.status(200).json(validationResult.data);
|
|
51
|
+
return res.status(200).json(validationResult.data);
|
|
46
52
|
} else {
|
|
47
|
-
res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
53
|
+
return res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
48
54
|
}
|
|
49
|
-
} else if (!req.omitOutputValidation) {
|
|
50
|
-
throw new Error('Output validation schema must be provided unless explicitly omitted.');
|
|
51
55
|
} else {
|
|
52
|
-
res.status(200).json(data);
|
|
56
|
+
return res.status(200).json(data);
|
|
53
57
|
}
|
|
54
58
|
} catch (error: unknown) {
|
|
55
|
-
|
|
56
|
-
if (error instanceof Error) {
|
|
57
|
-
res.status(500).json({ error: error.message });
|
|
58
|
-
} else {
|
|
59
|
-
res.status(500).json({ error: "Unknown error occurred" });
|
|
60
|
-
}
|
|
61
|
-
next(error);
|
|
59
|
+
return next(error);
|
|
62
60
|
}
|
|
63
61
|
}`
|
|
64
62
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DMMF } from '@prisma/generator-helper'
|
|
2
2
|
import { lowercaseFirstLetter } from '../utils/strings'
|
|
3
|
+
|
|
3
4
|
/**
|
|
4
5
|
* Generates an Express middleware function that handles batch deleting records
|
|
5
6
|
* and includes conditional output validation with Zod.
|
|
@@ -27,36 +28,35 @@ interface DeleteManyRequest extends Request {
|
|
|
27
28
|
body: ${argsTypeName};
|
|
28
29
|
outputValidation?: ZodTypeAny;
|
|
29
30
|
omitOutputValidation?: boolean;
|
|
31
|
+
locals?: {
|
|
32
|
+
outputValidator?: ZodTypeAny;
|
|
33
|
+
};
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
export type DeleteManyMiddleware = RequestHandler<ParamsDictionary, any,
|
|
36
|
+
export type DeleteManyMiddleware = RequestHandler<ParamsDictionary, any, ${argsTypeName}, Record<string, any>>;
|
|
33
37
|
|
|
34
38
|
export async function ${functionName}(req: DeleteManyRequest, res: Response, next: NextFunction) {
|
|
35
39
|
try {
|
|
36
|
-
|
|
40
|
+
const outputValidator = req.locals?.outputValidator || req.outputValidation;
|
|
41
|
+
|
|
42
|
+
if (!outputValidator && !req.omitOutputValidation) {
|
|
37
43
|
throw new Error('Output validation schema or omission flag must be provided.');
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
const result = await req.prisma.${lowercaseFirstLetter(modelName)}.deleteMany(req.body);
|
|
41
47
|
|
|
42
|
-
if (!req.omitOutputValidation &&
|
|
43
|
-
const validationResult =
|
|
48
|
+
if (!req.omitOutputValidation && outputValidator) {
|
|
49
|
+
const validationResult = outputValidator.safeParse(result);
|
|
44
50
|
if (validationResult.success) {
|
|
45
|
-
res.status(200).json(validationResult.data);
|
|
51
|
+
return res.status(200).json(validationResult.data);
|
|
46
52
|
} else {
|
|
47
|
-
res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
53
|
+
return res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
48
54
|
}
|
|
49
55
|
} else {
|
|
50
|
-
res.status(200).json(result);
|
|
56
|
+
return res.status(200).json(result);
|
|
51
57
|
}
|
|
52
58
|
} catch (error: unknown) {
|
|
53
|
-
|
|
54
|
-
if (error instanceof Error) {
|
|
55
|
-
res.status(500).json({ error: error.message });
|
|
56
|
-
} else {
|
|
57
|
-
res.status(500).json({ error: "Unknown error occurred" });
|
|
58
|
-
}
|
|
59
|
-
next(error);
|
|
59
|
+
return next(error);
|
|
60
60
|
}
|
|
61
61
|
}`
|
|
62
62
|
}
|
|
@@ -33,13 +33,16 @@ export interface FindFirstRequest extends Request {
|
|
|
33
33
|
passToNext?: boolean;
|
|
34
34
|
locals?: {
|
|
35
35
|
data?: ${modelName} | null
|
|
36
|
+
outputValidator?: ZodTypeAny;
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
export type FindFirstMiddleware = RequestHandler<ParamsDictionary, any, any, ${queryTypeName} & ParsedQs, Record<string, any>>
|
|
39
40
|
|
|
40
41
|
export async function ${functionName}(req: FindFirstRequest, res: Response, next: NextFunction) {
|
|
41
42
|
try {
|
|
42
|
-
|
|
43
|
+
const outputValidator = req.locals?.outputValidator || req.outputValidation;
|
|
44
|
+
|
|
45
|
+
if (!outputValidator && !req.omitOutputValidation) {
|
|
43
46
|
throw new Error('Output validation schema or omission flag must be provided.');
|
|
44
47
|
}
|
|
45
48
|
|
|
@@ -47,26 +50,18 @@ export async function ${functionName}(req: FindFirstRequest, res: Response, next
|
|
|
47
50
|
if (req.passToNext) {
|
|
48
51
|
if (req.locals) req.locals.data = data;
|
|
49
52
|
next();
|
|
50
|
-
} else if (!req.omitOutputValidation &&
|
|
51
|
-
const validationResult =
|
|
53
|
+
} else if (!req.omitOutputValidation && outputValidator) {
|
|
54
|
+
const validationResult = outputValidator.safeParse(data);
|
|
52
55
|
if (validationResult.success) {
|
|
53
|
-
res.status(200).json(validationResult.data);
|
|
56
|
+
return res.status(200).json(validationResult.data);
|
|
54
57
|
} else {
|
|
55
|
-
res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
58
|
+
return res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
56
59
|
}
|
|
57
|
-
} else if (!req.omitOutputValidation) {
|
|
58
|
-
throw new Error('Output validation schema must be provided unless explicitly omitted. Attach omitOutputValidation = true to request to suppress this error.');
|
|
59
60
|
} else {
|
|
60
|
-
res.status(200).json(data);
|
|
61
|
+
return res.status(200).json(data);
|
|
61
62
|
}
|
|
62
63
|
} catch (error: unknown) {
|
|
63
|
-
|
|
64
|
-
if (error instanceof Error) {
|
|
65
|
-
res.status(500).json({ error: error.message });
|
|
66
|
-
} else {
|
|
67
|
-
res.status(500).json({ error: "Unknown error occurred" });
|
|
68
|
-
}
|
|
69
|
-
next(error);
|
|
64
|
+
return next(error);
|
|
70
65
|
}
|
|
71
66
|
}`
|
|
72
67
|
}
|
|
@@ -33,13 +33,16 @@ export interface FindManyRequest extends Request {
|
|
|
33
33
|
passToNext?: boolean;
|
|
34
34
|
locals?: {
|
|
35
35
|
data?: ${modelName}[]
|
|
36
|
+
outputValidator?: ZodTypeAny;
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
export type FindManyMiddleware = RequestHandler<ParamsDictionary, any, any, ${queryTypeName} & ParsedQs, Record<string, any>>
|
|
39
40
|
|
|
40
41
|
export async function ${functionName}(req: FindManyRequest, res: Response, next: NextFunction) {
|
|
41
42
|
try {
|
|
42
|
-
|
|
43
|
+
const outputValidator = req.locals?.outputValidator || req.outputValidation;
|
|
44
|
+
|
|
45
|
+
if (!outputValidator && !req.omitOutputValidation) {
|
|
43
46
|
throw new Error('Output validation schema or omission flag must be provided.');
|
|
44
47
|
}
|
|
45
48
|
|
|
@@ -47,26 +50,18 @@ export async function ${functionName}(req: FindManyRequest, res: Response, next:
|
|
|
47
50
|
if (req.passToNext) {
|
|
48
51
|
if (req.locals) req.locals.data = data;
|
|
49
52
|
next();
|
|
50
|
-
} else if (!req.omitOutputValidation &&
|
|
51
|
-
const validationResult =
|
|
53
|
+
} else if (!req.omitOutputValidation && outputValidator) {
|
|
54
|
+
const validationResult = outputValidator.safeParse(data);
|
|
52
55
|
if (validationResult.success) {
|
|
53
|
-
res.status(200).json(validationResult.data);
|
|
56
|
+
return res.status(200).json(validationResult.data);
|
|
54
57
|
} else {
|
|
55
|
-
res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
58
|
+
return res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
56
59
|
}
|
|
57
|
-
} else if (!req.omitOutputValidation) {
|
|
58
|
-
throw new Error('Output validation schema must be provided unless explicitly omitted. Attach omitOutputValidation = true to request to suppress this error.');
|
|
59
60
|
} else {
|
|
60
|
-
res.status(200).json(data);
|
|
61
|
+
return res.status(200).json(data);
|
|
61
62
|
}
|
|
62
63
|
} catch (error: unknown) {
|
|
63
|
-
|
|
64
|
-
if (error instanceof Error) {
|
|
65
|
-
res.status(500).json({ error: error.message });
|
|
66
|
-
} else {
|
|
67
|
-
res.status(500).json({ error: "Unknown error occurred" });
|
|
68
|
-
}
|
|
69
|
-
next(error);
|
|
64
|
+
return next(error);
|
|
70
65
|
}
|
|
71
66
|
}`
|
|
72
67
|
}
|
|
@@ -33,13 +33,16 @@ export interface FindUniqueRequest extends Request {
|
|
|
33
33
|
passToNext?: boolean;
|
|
34
34
|
locals?: {
|
|
35
35
|
data?: ${modelName} | null
|
|
36
|
+
outputValidator?: ZodTypeAny;
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
export type FindUniqueMiddleware = RequestHandler<ParamsDictionary, any, any, ${queryTypeName} & ParsedQs, Record<string, any>>
|
|
39
40
|
|
|
40
41
|
export async function ${functionName}(req: FindUniqueRequest, res: Response, next: NextFunction) {
|
|
41
42
|
try {
|
|
42
|
-
|
|
43
|
+
const outputValidator = req.locals?.outputValidator || req.outputValidation;
|
|
44
|
+
|
|
45
|
+
if (!outputValidator && !req.omitOutputValidation) {
|
|
43
46
|
throw new Error('Output validation schema or omission flag must be provided.');
|
|
44
47
|
}
|
|
45
48
|
|
|
@@ -47,26 +50,18 @@ export async function ${functionName}(req: FindUniqueRequest, res: Response, nex
|
|
|
47
50
|
if (req.passToNext) {
|
|
48
51
|
if (req.locals) req.locals.data = data;
|
|
49
52
|
next();
|
|
50
|
-
} else if (!req.omitOutputValidation &&
|
|
51
|
-
const validationResult =
|
|
53
|
+
} else if (!req.omitOutputValidation && outputValidator) {
|
|
54
|
+
const validationResult = outputValidator.safeParse(data);
|
|
52
55
|
if (validationResult.success) {
|
|
53
|
-
res.status(200).json(validationResult.data);
|
|
56
|
+
return res.status(200).json(validationResult.data);
|
|
54
57
|
} else {
|
|
55
|
-
res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
58
|
+
return res.status(400).json({ error: 'Invalid data format', details: validationResult.error });
|
|
56
59
|
}
|
|
57
|
-
} else if (!req.omitOutputValidation) {
|
|
58
|
-
throw new Error('Output validation schema must be provided unless explicitly omitted. Attach omitOutputValidation = true to request to suppress this error.');
|
|
59
60
|
} else {
|
|
60
|
-
res.status(200).json(data);
|
|
61
|
+
return res.status(200).json(data);
|
|
61
62
|
}
|
|
62
63
|
} catch (error: unknown) {
|
|
63
|
-
|
|
64
|
-
if (error instanceof Error) {
|
|
65
|
-
res.status(500).json({ error: error.message });
|
|
66
|
-
} else {
|
|
67
|
-
res.status(500).json({ error: "Unknown error occurred" });
|
|
68
|
-
}
|
|
69
|
-
next(error);
|
|
64
|
+
return next(error);
|
|
70
65
|
}
|
|
71
66
|
}`
|
|
72
67
|
}
|