prisma-generator-express 1.56.4 → 1.58.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.
Files changed (44) hide show
  1. package/README.md +286 -29
  2. package/dist/copy/misc.js +25 -6
  3. package/dist/copy/misc.js.map +1 -1
  4. package/dist/generators/generateFastifyHandler.js +11 -0
  5. package/dist/generators/generateFastifyHandler.js.map +1 -1
  6. package/dist/generators/generateHonoHandler.js +14 -20
  7. package/dist/generators/generateHonoHandler.js.map +1 -1
  8. package/dist/generators/generateImportPrismaStatement.js +43 -0
  9. package/dist/generators/generateImportPrismaStatement.js.map +1 -1
  10. package/dist/generators/generateOperationCore.js +58 -17
  11. package/dist/generators/generateOperationCore.js.map +1 -1
  12. package/dist/generators/generateRouteConfigType.js +44 -15
  13. package/dist/generators/generateRouteConfigType.js.map +1 -1
  14. package/dist/generators/generateRouter.d.ts +2 -1
  15. package/dist/generators/generateRouter.js +60 -34
  16. package/dist/generators/generateRouter.js.map +1 -1
  17. package/dist/generators/generateRouterFastify.d.ts +2 -1
  18. package/dist/generators/generateRouterFastify.js +238 -193
  19. package/dist/generators/generateRouterFastify.js.map +1 -1
  20. package/dist/generators/generateRouterHono.d.ts +2 -1
  21. package/dist/generators/generateRouterHono.js +124 -89
  22. package/dist/generators/generateRouterHono.js.map +1 -1
  23. package/dist/index.js +22 -4
  24. package/dist/index.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/copy/autoIncludeRuntime.ts +9 -5
  27. package/src/copy/buildModelOpenApi.ts +96 -0
  28. package/src/copy/docsRenderer.ts +577 -174
  29. package/src/copy/materializedRouter.ts +40 -1
  30. package/src/copy/misc.ts +23 -6
  31. package/src/copy/operationDefinitions.ts +10 -0
  32. package/src/copy/operationRuntime.ts +28 -9
  33. package/src/copy/routeConfig.express.ts +9 -9
  34. package/src/copy/routeConfig.hono.ts +63 -5
  35. package/src/copy/routeConfig.ts +44 -20
  36. package/src/generators/generateFastifyHandler.ts +12 -0
  37. package/src/generators/generateHonoHandler.ts +15 -20
  38. package/src/generators/generateImportPrismaStatement.ts +13 -0
  39. package/src/generators/generateOperationCore.ts +58 -17
  40. package/src/generators/generateRouteConfigType.ts +52 -17
  41. package/src/generators/generateRouter.ts +61 -33
  42. package/src/generators/generateRouterFastify.ts +239 -192
  43. package/src/generators/generateRouterHono.ts +125 -88
  44. package/src/index.ts +25 -5
@@ -4,18 +4,38 @@ import type { Target } from '../constants'
4
4
  import { capitalize } from '../utils/strings'
5
5
 
6
6
  const ROUTER_OPERATIONS = [
7
- 'findUnique', 'findUniqueOrThrow', 'findFirst', 'findFirstOrThrow',
8
- 'findMany', 'findManyPaginated', 'count', 'aggregate', 'groupBy',
9
- 'create', 'createMany', 'createManyAndReturn',
10
- 'update', 'updateMany', 'updateManyAndReturn',
11
- 'upsert', 'delete', 'deleteMany',
7
+ 'findUnique',
8
+ 'findUniqueOrThrow',
9
+ 'findFirst',
10
+ 'findFirstOrThrow',
11
+ 'findMany',
12
+ 'findManyPaginated',
13
+ 'count',
14
+ 'aggregate',
15
+ 'groupBy',
16
+ 'create',
17
+ 'createMany',
18
+ 'createManyAndReturn',
19
+ 'update',
20
+ 'updateMany',
21
+ 'updateManyAndReturn',
22
+ 'upsert',
23
+ 'delete',
24
+ 'deleteMany',
12
25
  ] as const
13
26
 
14
27
  type RouterOperation = (typeof ROUTER_OPERATIONS)[number]
15
28
 
16
29
  const READ_OPERATIONS: ReadonlySet<RouterOperation> = new Set<RouterOperation>([
17
- 'findUnique', 'findUniqueOrThrow', 'findFirst', 'findFirstOrThrow',
18
- 'findMany', 'findManyPaginated', 'count', 'aggregate', 'groupBy',
30
+ 'findUnique',
31
+ 'findUniqueOrThrow',
32
+ 'findFirst',
33
+ 'findFirstOrThrow',
34
+ 'findMany',
35
+ 'findManyPaginated',
36
+ 'count',
37
+ 'aggregate',
38
+ 'groupBy',
19
39
  ])
20
40
 
21
41
  const ROUTER_OP_TO_SHAPE_OP: Record<RouterOperation, string> = {
@@ -62,8 +82,13 @@ function routeConfigBaseFor(target: Target): string {
62
82
  return `RouteConfig<Record<string, unknown>, TCtx>`
63
83
  }
64
84
 
65
- function hookHandlerTypeRef(target: Target, hookHandlerType: string): string {
66
- if (target === 'hono') return `${hookHandlerType}<TEnv>`
85
+ function beforeHookRef(target: Target, hookHandlerType: string): string {
86
+ if (target === 'hono') return `HonoBeforeHook<TEnv>`
87
+ return hookHandlerType
88
+ }
89
+
90
+ function afterHookRef(target: Target, hookHandlerType: string): string {
91
+ if (target === 'hono') return `HonoAfterHook<TEnv>`
67
92
  return hookHandlerType
68
93
  }
69
94
 
@@ -80,7 +105,8 @@ export function generateRouteConfigType(
80
105
 
81
106
  const generics = configGenericsFor(target)
82
107
  const baseConfig = routeConfigBaseFor(target)
83
- const hookRef = hookHandlerTypeRef(target, hookHandlerType)
108
+ const beforeRef = beforeHookRef(target, hookHandlerType)
109
+ const afterRef = afterHookRef(target, hookHandlerType)
84
110
  const requestType = requestTypeFor(target)
85
111
 
86
112
  const progressiveTypeImport = supportsProgressive
@@ -88,27 +114,36 @@ export function generateRouteConfigType(
88
114
  : ''
89
115
 
90
116
  if (!guardShapesImport) {
91
- return progressiveTypeImport + `export type ${m}RouteConfig${generics} = ${baseConfig}\n`
117
+ return (
118
+ progressiveTypeImport +
119
+ `export type ${m}RouteConfig${generics} = ${baseConfig}\n`
120
+ )
92
121
  }
93
122
 
94
- const shapeOps = Object.values(ROUTER_OP_TO_SHAPE_OP).filter((v, i, a) => a.indexOf(v) === i)
95
- const opShapeImports = shapeOps.map((op) => `${m}${capitalize(op)}ShapeInput`).join(',\n ')
123
+ const shapeOps = Object.values(ROUTER_OP_TO_SHAPE_OP).filter(
124
+ (v, i, a) => a.indexOf(v) === i,
125
+ )
126
+ const opShapeImports = shapeOps
127
+ .map((op) => `${m}${capitalize(op)}ShapeInput`)
128
+ .join(',\n ')
96
129
 
97
130
  const overrides = ROUTER_OPERATIONS.map((routerOp) => {
98
131
  const shapeOp = ROUTER_OP_TO_SHAPE_OP[routerOp]
99
132
  const c = capitalize(shapeOp)
100
133
  const isRead = READ_OPERATIONS.has(routerOp)
101
134
  const lines = [
102
- ` before?: ${hookRef}[]`,
103
- ` after?: ${hookRef}[]`,
135
+ ` before?: ${beforeRef}[]`,
136
+ ` after?: ${afterRef}[]`,
104
137
  ` shape?: ${m}${c}ShapeInput<TCtx>`,
105
138
  ` pagination?: Partial<PaginationConfig>`,
106
139
  ]
107
140
  if (isRead && supportsProgressive) {
108
141
  lines.push(` progressive?: Record<string, ProgressiveVariantConfig>`)
109
- lines.push(` progressiveStages?: Record<string, ProgressiveStage<TCtx, TPrisma>>`)
142
+ lines.push(
143
+ ` progressiveStages?: Record<string, ProgressiveStage<TCtx, TPrisma>>`,
144
+ )
110
145
  }
111
- return ` ${routerOp}?: {\n${lines.join('\n')}\n }`
146
+ return ` ${routerOp}?: {\n${lines.join('\n')}\n } | false`
112
147
  }).join('\n')
113
148
 
114
149
  const omitKeys = ROUTER_OPERATIONS.map((k) => `'${k}'`).join('\n | ')
@@ -11,6 +11,7 @@ export function generateRouterFunction({
11
11
  importStyle,
12
12
  writeStrategy,
13
13
  findManyPaginatedMode,
14
+ dropGuard,
14
15
  }: {
15
16
  model: DMMF.Model
16
17
  enums: DMMF.DatamodelEnum[]
@@ -18,6 +19,7 @@ export function generateRouterFunction({
18
19
  importStyle: ImportStyle
19
20
  writeStrategy: WriteStrategy
20
21
  findManyPaginatedMode: FindManyPaginatedMode
22
+ dropGuard: boolean
21
23
  }): string {
22
24
  const ext = importExt(importStyle)
23
25
  const modelName = model.name
@@ -82,6 +84,7 @@ import type {
82
84
  import { parseQueryParams } from '../parseQueryParams${ext}'
83
85
  import { sanitizeKeys, normalizePrefix, getEnv } from '../misc${ext}'
84
86
  import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
87
+ import { validateCountSourceWhere } from '../routeConfig${ext}'
85
88
  import type { OperationContext } from '../operationRuntime${ext}'
86
89
  import {
87
90
  transformResult,
@@ -102,6 +105,7 @@ const _env = getEnv()
102
105
 
103
106
  const WRITE_STRATEGY: WriteStrategy = '${writeStrategy}'
104
107
  const FIND_MANY_PAGINATED_MODE: FindManyPaginatedMode = '${findManyPaginatedMode}'
108
+ const DROP_GUARD = ${dropGuard} || _env.E2E === 'true'
105
109
 
106
110
  const MODEL_FIELDS = ${JSON.stringify(fieldsMeta, null, 2)} as const
107
111
  const MODEL_ENUMS = ${JSON.stringify(enumsMeta, null, 2)} as const
@@ -152,8 +156,16 @@ function readLocals(res: Response): LocalsBag {
152
156
  }
153
157
 
154
158
  export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${modelName}RouteConfig<TCtx, TPrisma> = {}) {
159
+ validateCountSourceWhere(config.pagination?.countSource, '${modelName} pagination')
160
+ validateCountSourceWhere(
161
+ (config.findManyPaginated && typeof config.findManyPaginated === 'object' ? config.findManyPaginated : undefined)?.pagination?.countSource,
162
+ '${modelName} findManyPaginated pagination',
163
+ )
164
+
155
165
  const router = express.Router()
156
166
 
167
+ const isEnabled = (value: unknown): boolean => value !== false && !!(config.enableAll || value)
168
+
157
169
  const customPrefix = normalizePrefix(config.customUrlPrefix || '')
158
170
  const modelPrefix = config.addModelPrefix !== false ? '/${modelNameLower}' : ''
159
171
  const basePath = customPrefix + modelPrefix
@@ -163,24 +175,32 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
163
175
 
164
176
  const postReadsEnabled = !config.disablePostReads
165
177
 
166
- const openApiJsonSpec = openApiDisabled
167
- ? null
168
- : buildModelOpenApi(
178
+ let _openApiJsonCache: unknown = undefined
179
+ const getOpenApiJson = (): unknown => {
180
+ if (_openApiJsonCache === undefined) {
181
+ _openApiJsonCache = buildModelOpenApi(
169
182
  '${modelName}',
170
183
  MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
171
184
  MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
172
185
  config as unknown as Parameters<typeof buildModelOpenApi>[3],
173
186
  { format: 'json', writeStrategy: WRITE_STRATEGY },
174
187
  )
175
- const openApiYamlSpec = openApiDisabled
176
- ? null
177
- : buildModelOpenApi(
188
+ }
189
+ return _openApiJsonCache
190
+ }
191
+ let _openApiYamlCache: string | undefined = undefined
192
+ const getOpenApiYaml = (): string => {
193
+ if (_openApiYamlCache === undefined) {
194
+ _openApiYamlCache = buildModelOpenApi(
178
195
  '${modelName}',
179
196
  MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
180
197
  MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
181
198
  config as unknown as Parameters<typeof buildModelOpenApi>[3],
182
199
  { format: 'yaml', writeStrategy: WRITE_STRATEGY },
183
- )
200
+ ) as string
201
+ }
202
+ return _openApiYamlCache
203
+ }
184
204
 
185
205
  const qbEnabled = isQueryBuilderEnabled(config)
186
206
  if (qbEnabled) {
@@ -221,7 +241,7 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
221
241
 
222
242
  const parseBodyAsQuery: RequestHandler = (req, res, next) => {
223
243
  if (!req.body || typeof req.body !== 'object' || Array.isArray(req.body)) {
224
- return next({ status: 400, message: 'Request body must be a JSON object' })
244
+ return next(new HttpError(400, 'Request body must be a JSON object'))
225
245
  }
226
246
  readLocals(res).parsedQuery = sanitizeKeys(req.body as Record<string, unknown>)
227
247
  next()
@@ -238,7 +258,7 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
238
258
  const headerValue = req.get(headerName)
239
259
  const caller = config.guard?.resolveVariant?.(req) ?? headerValue ?? undefined
240
260
  if (caller) locals.guardCaller = caller
241
- if (opConfig.shape) locals.guardShape = opConfig.shape
261
+ if (opConfig.shape && !DROP_GUARD) locals.guardShape = opConfig.shape
242
262
  next()
243
263
  }
244
264
  }
@@ -325,11 +345,13 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
325
345
  (name: string) => typeof stageRegistry[name] !== 'function',
326
346
  )
327
347
  if (missingStage) {
328
- return next({ status: 500, message: 'Missing progressive stage: ' + missingStage })
348
+ emitTerminalSSEError(res, 'Missing progressive stage: ' + missingStage)
349
+ return
329
350
  }
330
351
 
331
352
  if (typeof config.resolveContext !== 'function') {
332
- return next({ status: 500, message: 'Progressive endpoint requires config.resolveContext' })
353
+ emitTerminalSSEError(res, 'Progressive endpoint requires config.resolveContext')
354
+ return
333
355
  }
334
356
 
335
357
  const ctx = await config.resolveContext(req)
@@ -344,8 +366,8 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
344
366
  })
345
367
  } catch (err) {
346
368
  console.error('[progressive] dispatch error:', err)
347
- if (!res.headersSent) {
348
- return next({ status: 500, message: 'Internal server error' })
369
+ if (!res.headersSent && !res.writableEnded) {
370
+ emitTerminalSSEError(res, 'Internal server error')
349
371
  }
350
372
  }
351
373
  }
@@ -367,70 +389,70 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
367
389
  const openapiJsonPath = basePath ? \`\${basePath}/openapi.json\` : '/openapi.json'
368
390
  const openapiYamlPath = basePath ? \`\${basePath}/openapi.yaml\` : '/openapi.yaml'
369
391
  router.get(openapiJsonPath, (_req, res) => {
370
- res.json(openApiJsonSpec)
392
+ res.json(getOpenApiJson())
371
393
  })
372
394
  router.get(openapiYamlPath, (_req, res) => {
373
- res.type('application/yaml').send(openApiYamlSpec as string)
395
+ res.type('application/yaml').send(getOpenApiYaml())
374
396
  })
375
397
  }
376
398
 
377
- if (config.enableAll || config.findFirst) {
399
+ if (isEnabled(config.findFirst)) {
378
400
  const opConfig: OperationConfigLike = (config.findFirst as OperationConfigLike | undefined) ?? defaultOpConfig
379
401
  const { before = [], after = [] } = opConfig
380
402
  const path = basePath ? \`\${basePath}/first\` : '/first'
381
403
  router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.findFirst, 'findFirst'), ${modelName}FindFirst as RequestHandler, ...after, respond)
382
404
  if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${modelName}FindFirst as RequestHandler, ...after, respond)
383
405
  }
384
- if (config.enableAll || config.findFirstOrThrow) {
406
+ if (isEnabled(config.findFirstOrThrow)) {
385
407
  const opConfig: OperationConfigLike = (config.findFirstOrThrow as OperationConfigLike | undefined) ?? defaultOpConfig
386
408
  const { before = [], after = [] } = opConfig
387
409
  const path = basePath ? \`\${basePath}/first/strict\` : '/first/strict'
388
410
  router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.findFirstOrThrow, 'findFirstOrThrow'), ${modelName}FindFirstOrThrow as RequestHandler, ...after, respond)
389
411
  if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${modelName}FindFirstOrThrow as RequestHandler, ...after, respond)
390
412
  }
391
- if (config.enableAll || config.findManyPaginated) {
413
+ if (isEnabled(config.findManyPaginated)) {
392
414
  const opConfig: OperationConfigLike = (config.findManyPaginated as OperationConfigLike | undefined) ?? defaultOpConfig
393
415
  const { before = [], after = [] } = opConfig
394
416
  const path = basePath ? \`\${basePath}/paginated\` : '/paginated'
395
417
  router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.findManyPaginated, 'findManyPaginated'), ${modelName}FindManyPaginated as RequestHandler, ...after, respond)
396
418
  if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${modelName}FindManyPaginated as RequestHandler, ...after, respond)
397
419
  }
398
- if (config.enableAll || config.aggregate) {
420
+ if (isEnabled(config.aggregate)) {
399
421
  const opConfig: OperationConfigLike = (config.aggregate as OperationConfigLike | undefined) ?? defaultOpConfig
400
422
  const { before = [], after = [] } = opConfig
401
423
  const path = basePath ? \`\${basePath}/aggregate\` : '/aggregate'
402
424
  router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.aggregate, 'aggregate'), ${modelName}Aggregate as RequestHandler, ...after, respond)
403
425
  if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${modelName}Aggregate as RequestHandler, ...after, respond)
404
426
  }
405
- if (config.enableAll || config.count) {
427
+ if (isEnabled(config.count)) {
406
428
  const opConfig: OperationConfigLike = (config.count as OperationConfigLike | undefined) ?? defaultOpConfig
407
429
  const { before = [], after = [] } = opConfig
408
430
  const path = basePath ? \`\${basePath}/count\` : '/count'
409
431
  router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.count, 'count'), ${modelName}Count as RequestHandler, ...after, respond)
410
432
  if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${modelName}Count as RequestHandler, ...after, respond)
411
433
  }
412
- if (config.enableAll || config.groupBy) {
434
+ if (isEnabled(config.groupBy)) {
413
435
  const opConfig: OperationConfigLike = (config.groupBy as OperationConfigLike | undefined) ?? defaultOpConfig
414
436
  const { before = [], after = [] } = opConfig
415
437
  const path = basePath ? \`\${basePath}/groupby\` : '/groupby'
416
438
  router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.groupBy, 'groupBy'), ${modelName}GroupBy as RequestHandler, ...after, respond)
417
439
  if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${modelName}GroupBy as RequestHandler, ...after, respond)
418
440
  }
419
- if (config.enableAll || config.findUniqueOrThrow) {
441
+ if (isEnabled(config.findUniqueOrThrow)) {
420
442
  const opConfig: OperationConfigLike = (config.findUniqueOrThrow as OperationConfigLike | undefined) ?? defaultOpConfig
421
443
  const { before = [], after = [] } = opConfig
422
444
  const path = basePath ? \`\${basePath}/unique/strict\` : '/unique/strict'
423
445
  router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.findUniqueOrThrow, 'findUniqueOrThrow'), ${modelName}FindUniqueOrThrow as RequestHandler, ...after, respond)
424
446
  if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${modelName}FindUniqueOrThrow as RequestHandler, ...after, respond)
425
447
  }
426
- if (config.enableAll || config.findUnique) {
448
+ if (isEnabled(config.findUnique)) {
427
449
  const opConfig: OperationConfigLike = (config.findUnique as OperationConfigLike | undefined) ?? defaultOpConfig
428
450
  const { before = [], after = [] } = opConfig
429
451
  const path = basePath ? \`\${basePath}/unique\` : '/unique'
430
452
  router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.findUnique, 'findUnique'), ${modelName}FindUnique as RequestHandler, ...after, respond)
431
453
  if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${modelName}FindUnique as RequestHandler, ...after, respond)
432
454
  }
433
- if (config.enableAll || config.findMany) {
455
+ if (isEnabled(config.findMany)) {
434
456
  const opConfig: OperationConfigLike = (config.findMany as OperationConfigLike | undefined) ?? defaultOpConfig
435
457
  const { before = [], after = [] } = opConfig
436
458
  const path = basePath || '/'
@@ -441,55 +463,55 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
441
463
  }
442
464
  }
443
465
 
444
- if (config.enableAll || config.createManyAndReturn) {
466
+ if (isEnabled(config.createManyAndReturn)) {
445
467
  const opConfig: OperationConfigLike = (config.createManyAndReturn as OperationConfigLike | undefined) ?? defaultOpConfig
446
468
  const { before = [], after = [] } = opConfig
447
469
  const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
448
470
  router.post(path, setShape(opConfig), ...before, ${modelName}CreateManyAndReturn as RequestHandler, ...after, respondCreated)
449
471
  }
450
- if (config.enableAll || config.createMany) {
472
+ if (isEnabled(config.createMany)) {
451
473
  const opConfig: OperationConfigLike = (config.createMany as OperationConfigLike | undefined) ?? defaultOpConfig
452
474
  const { before = [], after = [] } = opConfig
453
475
  const path = basePath ? \`\${basePath}/many\` : '/many'
454
476
  router.post(path, setShape(opConfig), ...before, ${modelName}CreateMany as RequestHandler, ...after, respondCreated)
455
477
  }
456
- if (config.enableAll || config.create) {
478
+ if (isEnabled(config.create)) {
457
479
  const opConfig: OperationConfigLike = (config.create as OperationConfigLike | undefined) ?? defaultOpConfig
458
480
  const { before = [], after = [] } = opConfig
459
481
  const path = basePath || '/'
460
482
  router.post(path, setShape(opConfig), ...before, ${modelName}Create as RequestHandler, ...after, respondCreated)
461
483
  }
462
- if (config.enableAll || config.updateManyAndReturn) {
484
+ if (isEnabled(config.updateManyAndReturn)) {
463
485
  const opConfig: OperationConfigLike = (config.updateManyAndReturn as OperationConfigLike | undefined) ?? defaultOpConfig
464
486
  const { before = [], after = [] } = opConfig
465
487
  const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
466
488
  router.put(path, setShape(opConfig), ...before, ${modelName}UpdateManyAndReturn as RequestHandler, ...after, respond)
467
489
  }
468
- if (config.enableAll || config.updateMany) {
490
+ if (isEnabled(config.updateMany)) {
469
491
  const opConfig: OperationConfigLike = (config.updateMany as OperationConfigLike | undefined) ?? defaultOpConfig
470
492
  const { before = [], after = [] } = opConfig
471
493
  const path = basePath ? \`\${basePath}/many\` : '/many'
472
494
  router.put(path, setShape(opConfig), ...before, ${modelName}UpdateMany as RequestHandler, ...after, respond)
473
495
  }
474
- if (config.enableAll || config.update) {
496
+ if (isEnabled(config.update)) {
475
497
  const opConfig: OperationConfigLike = (config.update as OperationConfigLike | undefined) ?? defaultOpConfig
476
498
  const { before = [], after = [] } = opConfig
477
499
  const path = basePath || '/'
478
500
  router.put(path, setShape(opConfig), ...before, ${modelName}Update as RequestHandler, ...after, respond)
479
501
  }
480
- if (config.enableAll || config.upsert) {
502
+ if (isEnabled(config.upsert)) {
481
503
  const opConfig: OperationConfigLike = (config.upsert as OperationConfigLike | undefined) ?? defaultOpConfig
482
504
  const { before = [], after = [] } = opConfig
483
505
  const path = basePath || '/'
484
506
  router.patch(path, setShape(opConfig), ...before, ${modelName}Upsert as RequestHandler, ...after, respond)
485
507
  }
486
- if (config.enableAll || config.deleteMany) {
508
+ if (isEnabled(config.deleteMany)) {
487
509
  const opConfig: OperationConfigLike = (config.deleteMany as OperationConfigLike | undefined) ?? defaultOpConfig
488
510
  const { before = [], after = [] } = opConfig
489
511
  const path = basePath ? \`\${basePath}/many\` : '/many'
490
512
  router.delete(path, setShape(opConfig), ...before, ${modelName}DeleteMany as RequestHandler, ...after, respond)
491
513
  }
492
- if (config.enableAll || config.delete) {
514
+ if (isEnabled(config.delete)) {
493
515
  const opConfig: OperationConfigLike = (config.delete as OperationConfigLike | undefined) ?? defaultOpConfig
494
516
  const { before = [], after = [] } = opConfig
495
517
  const path = basePath || '/'
@@ -497,6 +519,12 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
497
519
  }
498
520
  if (config.updateEach) {
499
521
  const opConfig: OperationConfigLike = (config.updateEach as OperationConfigLike | undefined) ?? defaultOpConfig
522
+ if ((!opConfig.before || opConfig.before.length === 0) && _env.NODE_ENV !== 'production') {
523
+ console.warn(
524
+ '[${modelName}Router] updateEach is enabled without a before hook. ' +
525
+ 'This endpoint bypasses guard shapes and should be protected by authentication middleware.',
526
+ )
527
+ }
500
528
  const { before = [], after = [] } = opConfig
501
529
  const path = basePath ? \`\${basePath}/each\` : '/each'
502
530
  router.post(