prisma-generator-express 1.57.0 → 1.58.1

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 (42) hide show
  1. package/README.md +111 -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 +7 -1
  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 +12 -7
  13. package/dist/generators/generateRouteConfigType.js.map +1 -1
  14. package/dist/generators/generateRouter.js +57 -32
  15. package/dist/generators/generateRouter.js.map +1 -1
  16. package/dist/generators/generateRouterFastify.js +235 -191
  17. package/dist/generators/generateRouterFastify.js.map +1 -1
  18. package/dist/generators/generateRouterHono.d.ts +2 -3
  19. package/dist/generators/generateRouterHono.js +131 -89
  20. package/dist/generators/generateRouterHono.js.map +1 -1
  21. package/dist/index.js +8 -6
  22. package/dist/index.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/copy/autoIncludeRuntime.ts +9 -5
  25. package/src/copy/buildModelOpenApi.ts +96 -0
  26. package/src/copy/docsRenderer.ts +577 -174
  27. package/src/copy/materializedRouter.ts +40 -1
  28. package/src/copy/misc.ts +23 -6
  29. package/src/copy/operationDefinitions.ts +10 -0
  30. package/src/copy/operationRuntime.ts +28 -9
  31. package/src/copy/routeConfig.express.ts +9 -9
  32. package/src/copy/routeConfig.hono.ts +63 -5
  33. package/src/copy/routeConfig.ts +44 -22
  34. package/src/generators/generateFastifyHandler.ts +12 -0
  35. package/src/generators/generateHonoHandler.ts +15 -20
  36. package/src/generators/generateImportPrismaStatement.ts +10 -1
  37. package/src/generators/generateOperationCore.ts +58 -17
  38. package/src/generators/generateRouteConfigType.ts +13 -8
  39. package/src/generators/generateRouter.ts +57 -32
  40. package/src/generators/generateRouterFastify.ts +235 -191
  41. package/src/generators/generateRouterHono.ts +131 -91
  42. package/src/index.ts +11 -7
@@ -70,6 +70,7 @@ import {
70
70
  ${modelName}Aggregate,
71
71
  ${modelName}Count,
72
72
  ${modelName}GroupBy,
73
+ ${modelName}UpdateEach,
73
74
  } from './${modelName}Handlers${ext}'
74
75
  import type {
75
76
  RouteConfig,
@@ -81,6 +82,7 @@ import type {
81
82
  import { parseQueryParams } from '../parseQueryParams${ext}'
82
83
  import { sanitizeKeys, normalizePrefix, getEnv } from '../misc${ext}'
83
84
  import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
85
+ import { validateCountSourceWhere } from '../routeConfig${ext}'
84
86
  import { mapError, transformResult, mergePaginationConfig, HttpError, type OperationContext } from '../operationRuntime${ext}'
85
87
 
86
88
  ${generateRouteConfigType(modelName, 'FastifyHookHandler', guardShapesImport, importStyle, 'fastify')}
@@ -194,6 +196,7 @@ function sendResult(request: FastifyRequest, reply: FastifyReply): void {
194
196
  }
195
197
 
196
198
  function sendError(reply: FastifyReply, error: unknown): void {
199
+ if (reply.sent) return
197
200
  const httpError = mapError(error)
198
201
  reply.code(httpError.status).send({ message: httpError.message })
199
202
  }
@@ -202,229 +205,270 @@ export async function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(
202
205
  fastify: FastifyInstance,
203
206
  config: ${modelName}RouteConfig<TCtx, TPrisma> = {},
204
207
  ) {
205
- const customPrefix = normalizePrefix(config.customUrlPrefix || '')
206
- const modelPrefix = config.addModelPrefix !== false ? '/${modelNameLower}' : ''
207
- const basePath = customPrefix + modelPrefix
208
-
209
- const openApiDisabled = config.disableOpenApi === true
210
- || (config.disableOpenApi !== false && (
211
- _env.DISABLE_OPENAPI === 'true'
212
- || _env.NODE_ENV === 'production'
213
- ))
214
-
215
- const postReadsEnabled = !config.disablePostReads
216
-
217
- const openApiJsonSpec = openApiDisabled
218
- ? null
219
- : buildModelOpenApi(
220
- '${modelName}',
221
- MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
222
- MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
223
- config,
224
- { format: 'json', writeStrategy: WRITE_STRATEGY },
225
- )
226
- const openApiYamlSpec = openApiDisabled
227
- ? null
228
- : buildModelOpenApi(
229
- '${modelName}',
230
- MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
231
- MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
232
- config,
233
- { format: 'yaml', writeStrategy: WRITE_STRATEGY },
234
- )
235
-
236
- const qbEnabled = isQueryBuilderEnabled(config)
237
-
238
- if (qbEnabled) {
239
- const qbConfig = getQueryBuilderConfig(config)
240
- if (qbConfig) {
241
- try {
242
- startQueryBuilder(qbConfig)
243
- } catch (err) {
244
- if (_env.NODE_ENV !== 'production') console.warn('[query-builder]', err)
208
+ validateCountSourceWhere(config.pagination?.countSource, '${modelName} pagination')
209
+ validateCountSourceWhere(
210
+ (config.findManyPaginated && typeof config.findManyPaginated === 'object' ? config.findManyPaginated : undefined)?.pagination?.countSource,
211
+ '${modelName} findManyPaginated pagination',
212
+ )
213
+
214
+ await fastify.register(async (instance) => {
215
+ const isEnabled = (value: unknown): boolean => value !== false && !!(config.enableAll || value)
216
+
217
+ const customPrefix = normalizePrefix(config.customUrlPrefix || '')
218
+ const modelPrefix = config.addModelPrefix !== false ? '/${modelNameLower}' : ''
219
+ const basePath = customPrefix + modelPrefix
220
+
221
+ const openApiDisabled = config.disableOpenApi === true
222
+ || (config.disableOpenApi !== false && (
223
+ _env.NODE_ENV === 'production'
224
+ || _env.DISABLE_OPENAPI === 'true'
225
+ ))
226
+
227
+ const postReadsEnabled = !config.disablePostReads
228
+
229
+ let _openApiJsonCache: unknown = undefined
230
+ const getOpenApiJson = (): unknown => {
231
+ if (_openApiJsonCache === undefined) {
232
+ _openApiJsonCache = buildModelOpenApi(
233
+ '${modelName}',
234
+ MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
235
+ MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
236
+ config,
237
+ { format: 'json', writeStrategy: WRITE_STRATEGY },
238
+ )
245
239
  }
240
+ return _openApiJsonCache
241
+ }
242
+ let _openApiYamlCache: string | undefined = undefined
243
+ const getOpenApiYaml = (): string => {
244
+ if (_openApiYamlCache === undefined) {
245
+ _openApiYamlCache = buildModelOpenApi(
246
+ '${modelName}',
247
+ MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
248
+ MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
249
+ config,
250
+ { format: 'yaml', writeStrategy: WRITE_STRATEGY },
251
+ ) as string
252
+ }
253
+ return _openApiYamlCache
246
254
  }
247
- }
248
255
 
249
- fastify.addHook('onRequest', async (request: FastifyRequest) => {
250
- (request as FastifyExtended & { findManyPaginatedMode?: FindManyPaginatedMode }).findManyPaginatedMode = FIND_MANY_PAGINATED_MODE
251
- })
256
+ const qbEnabled = isQueryBuilderEnabled(config)
252
257
 
253
- fastify.setErrorHandler((error: FastifyError, _request: FastifyRequest, reply: FastifyReply) => {
254
- const e = error as { status?: number; statusCode?: number; message?: string }
255
- const status = e.status ?? e.statusCode ?? 500
256
- const message = error.message || 'Internal server error'
257
- if (!reply.sent) {
258
- reply.code(status).send({ message })
258
+ if (qbEnabled) {
259
+ const qbConfig = getQueryBuilderConfig(config)
260
+ if (qbConfig) {
261
+ try {
262
+ startQueryBuilder(qbConfig)
263
+ } catch (err) {
264
+ if (_env.NODE_ENV !== 'production') console.warn('[query-builder]', err)
265
+ }
266
+ }
259
267
  }
260
- })
261
-
262
- if (!openApiDisabled) {
263
- const openapiJsonPath = basePath ? \`\${basePath}/openapi.json\` : '/openapi.json'
264
- const openapiYamlPath = basePath ? \`\${basePath}/openapi.yaml\` : '/openapi.yaml'
265
268
 
266
- fastify.get(openapiJsonPath, async (_request, reply) => {
267
- return reply.send(openApiJsonSpec)
269
+ instance.addHook('onRequest', async (request: FastifyRequest) => {
270
+ (request as FastifyExtended & { findManyPaginatedMode?: FindManyPaginatedMode }).findManyPaginatedMode = FIND_MANY_PAGINATED_MODE
268
271
  })
269
272
 
270
- fastify.get(openapiYamlPath, async (_request, reply) => {
271
- return reply.type('application/yaml').send(openApiYamlSpec as string)
273
+ instance.setErrorHandler((error: FastifyError, _request: FastifyRequest, reply: FastifyReply) => {
274
+ const e = error as { status?: number; statusCode?: number; message?: string }
275
+ const status = e.status ?? e.statusCode ?? 500
276
+ const message = error.message || 'Internal server error'
277
+ if (!reply.sent) {
278
+ reply.code(status).send({ message })
279
+ }
272
280
  })
273
- }
274
281
 
275
- const handleGet = (
276
- opConfig: OperationConfigLike,
277
- handlerFn: (req: FastifyRequest, reply: FastifyReply) => Promise<void>,
278
- parseFn: (req: FastifyRequest) => void,
279
- ) => async (request: FastifyRequest, reply: FastifyReply) => {
280
- try {
281
- parseFn(request)
282
- makeShapeHook(config, opConfig)(request)
283
- const { before = [], after = [] } = opConfig
284
- if (await runHooks(before, request, reply)) return
285
- await handlerFn(request, reply)
286
- if (await runHooks(after, request, reply)) return
287
- sendResult(request, reply)
288
- } catch (error: unknown) {
289
- sendError(reply, error)
282
+ if (!openApiDisabled) {
283
+ const openapiJsonPath = basePath ? \`\${basePath}/openapi.json\` : '/openapi.json'
284
+ const openapiYamlPath = basePath ? \`\${basePath}/openapi.yaml\` : '/openapi.yaml'
285
+
286
+ instance.get(openapiJsonPath, async (_request, reply) => {
287
+ return reply.send(getOpenApiJson())
288
+ })
289
+
290
+ instance.get(openapiYamlPath, async (_request, reply) => {
291
+ return reply.type('application/yaml').send(getOpenApiYaml())
292
+ })
290
293
  }
291
- }
292
294
 
293
- const handleWrite = (
294
- opConfig: OperationConfigLike,
295
- handlerFn: (req: FastifyRequest, reply: FastifyReply) => Promise<void>,
296
- ) => async (request: FastifyRequest, reply: FastifyReply) => {
297
- try {
298
- makeShapeHook(config, opConfig)(request)
299
- const { before = [], after = [] } = opConfig
300
- if (await runHooks(before, request, reply)) return
301
- await handlerFn(request, reply)
302
- if (await runHooks(after, request, reply)) return
303
- sendResult(request, reply)
304
- } catch (error: unknown) {
305
- sendError(reply, error)
295
+ const handleGet = (
296
+ opConfig: OperationConfigLike,
297
+ handlerFn: (req: FastifyRequest, reply: FastifyReply) => Promise<void>,
298
+ parseFn: (req: FastifyRequest) => void,
299
+ ) => async (request: FastifyRequest, reply: FastifyReply) => {
300
+ try {
301
+ parseFn(request)
302
+ makeShapeHook(config, opConfig)(request)
303
+ const { before = [], after = [] } = opConfig
304
+ if (await runHooks(before, request, reply)) return
305
+ await handlerFn(request, reply)
306
+ if (await runHooks(after, request, reply)) return
307
+ sendResult(request, reply)
308
+ } catch (error: unknown) {
309
+ sendError(reply, error)
310
+ }
306
311
  }
307
- }
308
312
 
309
- if (config.enableAll || config.findFirst) {
310
- const opConfig: OperationConfigLike = (config.findFirst as OperationConfigLike | undefined) ?? defaultOpConfig
311
- const path = basePath ? \`\${basePath}/first\` : '/first'
312
- fastify.get(path, handleGet(opConfig, ${modelName}FindFirst, parseQueryHook))
313
- if (postReadsEnabled) fastify.post(path, handleGet(opConfig, ${modelName}FindFirst, parseBodyAsQueryHook))
314
- }
313
+ const handleWrite = (
314
+ opConfig: OperationConfigLike,
315
+ handlerFn: (req: FastifyRequest, reply: FastifyReply) => Promise<void>,
316
+ ) => async (request: FastifyRequest, reply: FastifyReply) => {
317
+ try {
318
+ makeShapeHook(config, opConfig)(request)
319
+ const { before = [], after = [] } = opConfig
320
+ if (await runHooks(before, request, reply)) return
321
+ await handlerFn(request, reply)
322
+ if (await runHooks(after, request, reply)) return
323
+ sendResult(request, reply)
324
+ } catch (error: unknown) {
325
+ sendError(reply, error)
326
+ }
327
+ }
315
328
 
316
- if (config.enableAll || config.findFirstOrThrow) {
317
- const opConfig: OperationConfigLike = (config.findFirstOrThrow as OperationConfigLike | undefined) ?? defaultOpConfig
318
- const path = basePath ? \`\${basePath}/first/strict\` : '/first/strict'
319
- fastify.get(path, handleGet(opConfig, ${modelName}FindFirstOrThrow, parseQueryHook))
320
- if (postReadsEnabled) fastify.post(path, handleGet(opConfig, ${modelName}FindFirstOrThrow, parseBodyAsQueryHook))
321
- }
329
+ if (isEnabled(config.findFirst)) {
330
+ const opConfig: OperationConfigLike = (config.findFirst as OperationConfigLike | undefined) ?? defaultOpConfig
331
+ const path = basePath ? \`\${basePath}/first\` : '/first'
332
+ instance.get(path, handleGet(opConfig, ${modelName}FindFirst, parseQueryHook))
333
+ if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}FindFirst, parseBodyAsQueryHook))
334
+ }
322
335
 
323
- if (config.enableAll || config.findManyPaginated) {
324
- const opConfig: OperationConfigLike = (config.findManyPaginated as OperationConfigLike | undefined) ?? defaultOpConfig
325
- const path = basePath ? \`\${basePath}/paginated\` : '/paginated'
326
- fastify.get(path, handleGet(opConfig, ${modelName}FindManyPaginated, parseQueryHook))
327
- if (postReadsEnabled) fastify.post(path, handleGet(opConfig, ${modelName}FindManyPaginated, parseBodyAsQueryHook))
328
- }
336
+ if (isEnabled(config.findFirstOrThrow)) {
337
+ const opConfig: OperationConfigLike = (config.findFirstOrThrow as OperationConfigLike | undefined) ?? defaultOpConfig
338
+ const path = basePath ? \`\${basePath}/first/strict\` : '/first/strict'
339
+ instance.get(path, handleGet(opConfig, ${modelName}FindFirstOrThrow, parseQueryHook))
340
+ if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}FindFirstOrThrow, parseBodyAsQueryHook))
341
+ }
329
342
 
330
- if (config.enableAll || config.aggregate) {
331
- const opConfig: OperationConfigLike = (config.aggregate as OperationConfigLike | undefined) ?? defaultOpConfig
332
- const path = basePath ? \`\${basePath}/aggregate\` : '/aggregate'
333
- fastify.get(path, handleGet(opConfig, ${modelName}Aggregate, parseQueryHook))
334
- if (postReadsEnabled) fastify.post(path, handleGet(opConfig, ${modelName}Aggregate, parseBodyAsQueryHook))
335
- }
343
+ if (isEnabled(config.findManyPaginated)) {
344
+ const opConfig: OperationConfigLike = (config.findManyPaginated as OperationConfigLike | undefined) ?? defaultOpConfig
345
+ const path = basePath ? \`\${basePath}/paginated\` : '/paginated'
346
+ instance.get(path, handleGet(opConfig, ${modelName}FindManyPaginated, parseQueryHook))
347
+ if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}FindManyPaginated, parseBodyAsQueryHook))
348
+ }
336
349
 
337
- if (config.enableAll || config.count) {
338
- const opConfig: OperationConfigLike = (config.count as OperationConfigLike | undefined) ?? defaultOpConfig
339
- const path = basePath ? \`\${basePath}/count\` : '/count'
340
- fastify.get(path, handleGet(opConfig, ${modelName}Count, parseQueryHook))
341
- if (postReadsEnabled) fastify.post(path, handleGet(opConfig, ${modelName}Count, parseBodyAsQueryHook))
342
- }
350
+ if (isEnabled(config.aggregate)) {
351
+ const opConfig: OperationConfigLike = (config.aggregate as OperationConfigLike | undefined) ?? defaultOpConfig
352
+ const path = basePath ? \`\${basePath}/aggregate\` : '/aggregate'
353
+ instance.get(path, handleGet(opConfig, ${modelName}Aggregate, parseQueryHook))
354
+ if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}Aggregate, parseBodyAsQueryHook))
355
+ }
343
356
 
344
- if (config.enableAll || config.groupBy) {
345
- const opConfig: OperationConfigLike = (config.groupBy as OperationConfigLike | undefined) ?? defaultOpConfig
346
- const path = basePath ? \`\${basePath}/groupby\` : '/groupby'
347
- fastify.get(path, handleGet(opConfig, ${modelName}GroupBy, parseQueryHook))
348
- if (postReadsEnabled) fastify.post(path, handleGet(opConfig, ${modelName}GroupBy, parseBodyAsQueryHook))
349
- }
357
+ if (isEnabled(config.count)) {
358
+ const opConfig: OperationConfigLike = (config.count as OperationConfigLike | undefined) ?? defaultOpConfig
359
+ const path = basePath ? \`\${basePath}/count\` : '/count'
360
+ instance.get(path, handleGet(opConfig, ${modelName}Count, parseQueryHook))
361
+ if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}Count, parseBodyAsQueryHook))
362
+ }
350
363
 
351
- if (config.enableAll || config.findUniqueOrThrow) {
352
- const opConfig: OperationConfigLike = (config.findUniqueOrThrow as OperationConfigLike | undefined) ?? defaultOpConfig
353
- const path = basePath ? \`\${basePath}/unique/strict\` : '/unique/strict'
354
- fastify.get(path, handleGet(opConfig, ${modelName}FindUniqueOrThrow, parseQueryHook))
355
- if (postReadsEnabled) fastify.post(path, handleGet(opConfig, ${modelName}FindUniqueOrThrow, parseBodyAsQueryHook))
356
- }
364
+ if (isEnabled(config.groupBy)) {
365
+ const opConfig: OperationConfigLike = (config.groupBy as OperationConfigLike | undefined) ?? defaultOpConfig
366
+ const path = basePath ? \`\${basePath}/groupby\` : '/groupby'
367
+ instance.get(path, handleGet(opConfig, ${modelName}GroupBy, parseQueryHook))
368
+ if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}GroupBy, parseBodyAsQueryHook))
369
+ }
357
370
 
358
- if (config.enableAll || config.findUnique) {
359
- const opConfig: OperationConfigLike = (config.findUnique as OperationConfigLike | undefined) ?? defaultOpConfig
360
- const path = basePath ? \`\${basePath}/unique\` : '/unique'
361
- fastify.get(path, handleGet(opConfig, ${modelName}FindUnique, parseQueryHook))
362
- if (postReadsEnabled) fastify.post(path, handleGet(opConfig, ${modelName}FindUnique, parseBodyAsQueryHook))
363
- }
371
+ if (isEnabled(config.findUniqueOrThrow)) {
372
+ const opConfig: OperationConfigLike = (config.findUniqueOrThrow as OperationConfigLike | undefined) ?? defaultOpConfig
373
+ const path = basePath ? \`\${basePath}/unique/strict\` : '/unique/strict'
374
+ instance.get(path, handleGet(opConfig, ${modelName}FindUniqueOrThrow, parseQueryHook))
375
+ if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}FindUniqueOrThrow, parseBodyAsQueryHook))
376
+ }
364
377
 
365
- if (config.enableAll || config.findMany) {
366
- const opConfig: OperationConfigLike = (config.findMany as OperationConfigLike | undefined) ?? defaultOpConfig
367
- const path = basePath || '/'
368
- fastify.get(path, handleGet(opConfig, ${modelName}FindMany, parseQueryHook))
369
- if (postReadsEnabled) {
370
- const postPath = basePath ? \`\${basePath}/read\` : '/read'
371
- fastify.post(postPath, handleGet(opConfig, ${modelName}FindMany, parseBodyAsQueryHook))
378
+ if (isEnabled(config.findUnique)) {
379
+ const opConfig: OperationConfigLike = (config.findUnique as OperationConfigLike | undefined) ?? defaultOpConfig
380
+ const path = basePath ? \`\${basePath}/unique\` : '/unique'
381
+ instance.get(path, handleGet(opConfig, ${modelName}FindUnique, parseQueryHook))
382
+ if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}FindUnique, parseBodyAsQueryHook))
372
383
  }
373
- }
374
384
 
375
- if (config.enableAll || config.createManyAndReturn) {
376
- const opConfig: OperationConfigLike = (config.createManyAndReturn as OperationConfigLike | undefined) ?? defaultOpConfig
377
- const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
378
- fastify.post(path, handleWrite(opConfig, ${modelName}CreateManyAndReturn))
379
- }
385
+ if (isEnabled(config.findMany)) {
386
+ const opConfig: OperationConfigLike = (config.findMany as OperationConfigLike | undefined) ?? defaultOpConfig
387
+ const path = basePath || '/'
388
+ instance.get(path, handleGet(opConfig, ${modelName}FindMany, parseQueryHook))
389
+ if (postReadsEnabled) {
390
+ const postPath = basePath ? \`\${basePath}/read\` : '/read'
391
+ instance.post(postPath, handleGet(opConfig, ${modelName}FindMany, parseBodyAsQueryHook))
392
+ }
393
+ }
380
394
 
381
- if (config.enableAll || config.createMany) {
382
- const opConfig: OperationConfigLike = (config.createMany as OperationConfigLike | undefined) ?? defaultOpConfig
383
- const path = basePath ? \`\${basePath}/many\` : '/many'
384
- fastify.post(path, handleWrite(opConfig, ${modelName}CreateMany))
385
- }
395
+ if (isEnabled(config.createManyAndReturn)) {
396
+ const opConfig: OperationConfigLike = (config.createManyAndReturn as OperationConfigLike | undefined) ?? defaultOpConfig
397
+ const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
398
+ instance.post(path, handleWrite(opConfig, ${modelName}CreateManyAndReturn))
399
+ }
386
400
 
387
- if (config.enableAll || config.create) {
388
- const opConfig: OperationConfigLike = (config.create as OperationConfigLike | undefined) ?? defaultOpConfig
389
- const path = basePath || '/'
390
- fastify.post(path, handleWrite(opConfig, ${modelName}Create))
391
- }
401
+ if (isEnabled(config.createMany)) {
402
+ const opConfig: OperationConfigLike = (config.createMany as OperationConfigLike | undefined) ?? defaultOpConfig
403
+ const path = basePath ? \`\${basePath}/many\` : '/many'
404
+ instance.post(path, handleWrite(opConfig, ${modelName}CreateMany))
405
+ }
392
406
 
393
- if (config.enableAll || config.updateManyAndReturn) {
394
- const opConfig: OperationConfigLike = (config.updateManyAndReturn as OperationConfigLike | undefined) ?? defaultOpConfig
395
- const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
396
- fastify.put(path, handleWrite(opConfig, ${modelName}UpdateManyAndReturn))
397
- }
407
+ if (isEnabled(config.create)) {
408
+ const opConfig: OperationConfigLike = (config.create as OperationConfigLike | undefined) ?? defaultOpConfig
409
+ const path = basePath || '/'
410
+ instance.post(path, handleWrite(opConfig, ${modelName}Create))
411
+ }
398
412
 
399
- if (config.enableAll || config.updateMany) {
400
- const opConfig: OperationConfigLike = (config.updateMany as OperationConfigLike | undefined) ?? defaultOpConfig
401
- const path = basePath ? \`\${basePath}/many\` : '/many'
402
- fastify.put(path, handleWrite(opConfig, ${modelName}UpdateMany))
403
- }
413
+ if (isEnabled(config.updateManyAndReturn)) {
414
+ const opConfig: OperationConfigLike = (config.updateManyAndReturn as OperationConfigLike | undefined) ?? defaultOpConfig
415
+ const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
416
+ instance.put(path, handleWrite(opConfig, ${modelName}UpdateManyAndReturn))
417
+ }
404
418
 
405
- if (config.enableAll || config.update) {
406
- const opConfig: OperationConfigLike = (config.update as OperationConfigLike | undefined) ?? defaultOpConfig
407
- const path = basePath || '/'
408
- fastify.put(path, handleWrite(opConfig, ${modelName}Update))
409
- }
419
+ if (isEnabled(config.updateMany)) {
420
+ const opConfig: OperationConfigLike = (config.updateMany as OperationConfigLike | undefined) ?? defaultOpConfig
421
+ const path = basePath ? \`\${basePath}/many\` : '/many'
422
+ instance.put(path, handleWrite(opConfig, ${modelName}UpdateMany))
423
+ }
410
424
 
411
- if (config.enableAll || config.upsert) {
412
- const opConfig: OperationConfigLike = (config.upsert as OperationConfigLike | undefined) ?? defaultOpConfig
413
- const path = basePath || '/'
414
- fastify.patch(path, handleWrite(opConfig, ${modelName}Upsert))
415
- }
425
+ if (isEnabled(config.update)) {
426
+ const opConfig: OperationConfigLike = (config.update as OperationConfigLike | undefined) ?? defaultOpConfig
427
+ const path = basePath || '/'
428
+ instance.put(path, handleWrite(opConfig, ${modelName}Update))
429
+ }
416
430
 
417
- if (config.enableAll || config.deleteMany) {
418
- const opConfig: OperationConfigLike = (config.deleteMany as OperationConfigLike | undefined) ?? defaultOpConfig
419
- const path = basePath ? \`\${basePath}/many\` : '/many'
420
- fastify.delete(path, handleWrite(opConfig, ${modelName}DeleteMany))
421
- }
431
+ if (isEnabled(config.upsert)) {
432
+ const opConfig: OperationConfigLike = (config.upsert as OperationConfigLike | undefined) ?? defaultOpConfig
433
+ const path = basePath || '/'
434
+ instance.patch(path, handleWrite(opConfig, ${modelName}Upsert))
435
+ }
422
436
 
423
- if (config.enableAll || config.delete) {
424
- const opConfig: OperationConfigLike = (config.delete as OperationConfigLike | undefined) ?? defaultOpConfig
425
- const path = basePath || '/'
426
- fastify.delete(path, handleWrite(opConfig, ${modelName}Delete))
427
- }
437
+ if (isEnabled(config.deleteMany)) {
438
+ const opConfig: OperationConfigLike = (config.deleteMany as OperationConfigLike | undefined) ?? defaultOpConfig
439
+ const path = basePath ? \`\${basePath}/many\` : '/many'
440
+ instance.delete(path, handleWrite(opConfig, ${modelName}DeleteMany))
441
+ }
442
+
443
+ if (isEnabled(config.delete)) {
444
+ const opConfig: OperationConfigLike = (config.delete as OperationConfigLike | undefined) ?? defaultOpConfig
445
+ const path = basePath || '/'
446
+ instance.delete(path, handleWrite(opConfig, ${modelName}Delete))
447
+ }
448
+
449
+ if (config.updateEach) {
450
+ const opConfig: OperationConfigLike = (config.updateEach as OperationConfigLike | undefined) ?? defaultOpConfig
451
+ if ((!opConfig.before || opConfig.before.length === 0) && _env.NODE_ENV !== 'production') {
452
+ console.warn(
453
+ '[${modelName}Router] updateEach is enabled without a before hook. ' +
454
+ 'This endpoint bypasses guard shapes and should be protected by authentication middleware.',
455
+ )
456
+ }
457
+ const path = basePath ? \`\${basePath}/each\` : '/each'
458
+ instance.post(path, async (request: FastifyRequest, reply: FastifyReply) => {
459
+ try {
460
+ makeShapeHook(config, opConfig)(request)
461
+ const { before = [], after = [] } = opConfig
462
+ if (await runHooks(before, request, reply)) return
463
+ await ${modelName}UpdateEach(request, reply)
464
+ if (await runHooks(after, request, reply)) return
465
+ sendResult(request, reply)
466
+ } catch (error: unknown) {
467
+ sendError(reply, error)
468
+ }
469
+ })
470
+ }
471
+ })
428
472
  }
429
473
  `
430
474
  }