prisma-generator-express 1.38.0 → 1.40.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/dist/index.js CHANGED
@@ -7,8 +7,10 @@ const generator_helper_1 = require("@prisma/generator-helper");
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const generateUnifiedHandler_1 = require("./generators/generateUnifiedHandler");
9
9
  const generateFastifyHandler_1 = require("./generators/generateFastifyHandler");
10
+ const generateHonoHandler_1 = require("./generators/generateHonoHandler");
10
11
  const generateRouter_1 = require("./generators/generateRouter");
11
12
  const generateRouterFastify_1 = require("./generators/generateRouterFastify");
13
+ const generateRouterHono_1 = require("./generators/generateRouterHono");
12
14
  const generateUnifiedScalarUI_1 = require("./generators/generateUnifiedScalarUI");
13
15
  const generateUnifiedDocs_1 = require("./generators/generateUnifiedDocs");
14
16
  const generateQueryBuilderHelper_1 = require("./generators/generateQueryBuilderHelper");
@@ -19,9 +21,9 @@ const copyFiles_1 = require("./utils/copyFiles");
19
21
  const constants_1 = require("./constants");
20
22
  function getTarget(options) {
21
23
  const raw = String(options.generator.config.target ?? 'express').toLowerCase();
22
- if (raw === 'express' || raw === 'fastify')
24
+ if (raw === 'express' || raw === 'fastify' || raw === 'hono')
23
25
  return raw;
24
- throw new Error(`Invalid target "${raw}". Expected "express" or "fastify".`);
26
+ throw new Error(`Invalid target "${raw}". Expected "express", "fastify", or "hono".`);
25
27
  }
26
28
  (0, generator_helper_1.generatorHandler)({
27
29
  onManifest() {
@@ -50,7 +52,11 @@ function getTarget(options) {
50
52
  operation: 'operationRuntime',
51
53
  });
52
54
  const modelNames = [];
53
- const generateHandler = target === 'fastify' ? generateFastifyHandler_1.generateFastifyHandler : generateUnifiedHandler_1.generateUnifiedHandler;
55
+ const generateHandler = target === 'fastify'
56
+ ? generateFastifyHandler_1.generateFastifyHandler
57
+ : target === 'hono'
58
+ ? generateHonoHandler_1.generateHonoHandler
59
+ : generateUnifiedHandler_1.generateUnifiedHandler;
54
60
  for (const model of options.dmmf.datamodel.models) {
55
61
  if (model.documentation &&
56
62
  model.documentation.includes('generator off')) {
@@ -80,12 +86,18 @@ function getTarget(options) {
80
86
  enums: options.dmmf.datamodel.enums,
81
87
  guardShapesImport,
82
88
  })
83
- : (0, generateRouter_1.generateRouterFunction)({
84
- model: model,
85
- enums: options.dmmf.datamodel.enums,
86
- relativeClientPath,
87
- guardShapesImport,
88
- });
89
+ : target === 'hono'
90
+ ? (0, generateRouterHono_1.generateHonoRouterFunction)({
91
+ model: model,
92
+ enums: options.dmmf.datamodel.enums,
93
+ guardShapesImport,
94
+ })
95
+ : (0, generateRouter_1.generateRouterFunction)({
96
+ model: model,
97
+ enums: options.dmmf.datamodel.enums,
98
+ relativeClientPath,
99
+ guardShapesImport,
100
+ });
89
101
  await (0, writeFileSafely_1.writeFileSafely)({
90
102
  content: routerContent,
91
103
  options,
@@ -116,7 +128,12 @@ function getTarget(options) {
116
128
  console.log('\n═══ Generation Complete ═══');
117
129
  console.log(`✓ ${modelNames.length} models (${target})`);
118
130
  console.log(`✓ OpenAPI documentation generated`);
119
- console.log(`✓ Query builder helper generated`);
131
+ if (target === 'hono') {
132
+ console.log(`✓ Query builder helper generated (not auto-started for Hono)`);
133
+ }
134
+ else {
135
+ console.log(`✓ Query builder helper generated`);
136
+ }
120
137
  console.log('');
121
138
  },
122
139
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "prisma-generator-express",
3
- "description": "Prisma generator for Express/Fastify CRUD API with OpenAPI documentation",
4
- "version": "1.38.0",
3
+ "description": "Prisma generator for Express, Fastify, and Hono CRUD APIs with OpenAPI documentation",
4
+ "version": "1.40.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "MIT",
@@ -25,6 +25,7 @@
25
25
  "@prisma/client": ">=6.0.0",
26
26
  "express": ">=4.0.0",
27
27
  "fastify": "^5.0.0",
28
+ "hono": ">=4.0.0",
28
29
  "prisma-guard": ">=1.0.0",
29
30
  "prisma-query-builder-ui": ">=1.1.0",
30
31
  "prisma-sql": ">=1.0.0"
@@ -50,6 +51,7 @@
50
51
  "express": "^5.2.1",
51
52
  "fast-xml-parser": "^5.3.3",
52
53
  "fastify": "^5.6.2",
54
+ "hono": "^4.12.23",
53
55
  "lru-cache": "^11.2.4",
54
56
  "pino": "^10.1.0",
55
57
  "pino-pretty": "^13.0.0",
package/src/constants.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export const GENERATOR_NAME = 'prisma-generator-express'
2
2
 
3
- export type Target = 'express' | 'fastify'
3
+ export type Target = 'express' | 'fastify' | 'hono'
@@ -0,0 +1,21 @@
1
+ import type { Context, Next } from 'hono'
2
+ import type {
3
+ BaseOperationConfig,
4
+ BaseRouteConfig,
5
+ QueryBuilderConfig,
6
+ OpenApiServerConfig,
7
+ OpenApiSecuritySchemeConfig,
8
+ } from './routeConfig'
9
+
10
+ export type { QueryBuilderConfig, OpenApiServerConfig, OpenApiSecuritySchemeConfig }
11
+
12
+ export type HonoHookHandler<Env extends { Variables: any } = any> = (
13
+ c: Context<Env>,
14
+ next: Next,
15
+ ) => Promise<Response | void> | Response | void
16
+
17
+ export type OperationConfig<TShape = Record<string, any>> =
18
+ BaseOperationConfig<HonoHookHandler, TShape>
19
+
20
+ export type RouteConfig<TShape = Record<string, any>> =
21
+ BaseRouteConfig<HonoHookHandler, Context, TShape>
@@ -0,0 +1,104 @@
1
+ import { DMMF } from '@prisma/generator-helper'
2
+ import { toCamelCase } from '../utils/strings'
3
+
4
+ const CORE_NAME_MAP: Record<string, string> = {
5
+ delete: 'deleteUnique',
6
+ }
7
+
8
+ function coreFnName(op: string): string {
9
+ return CORE_NAME_MAP[op] || op
10
+ }
11
+
12
+ const READ_OPS = [
13
+ 'findMany',
14
+ 'findFirst',
15
+ 'findFirstOrThrow',
16
+ 'findUnique',
17
+ 'findUniqueOrThrow',
18
+ 'findManyPaginated',
19
+ 'aggregate',
20
+ 'count',
21
+ 'groupBy',
22
+ ]
23
+
24
+ const WRITE_OPS = [
25
+ 'create',
26
+ 'createMany',
27
+ 'createManyAndReturn',
28
+ 'update',
29
+ 'updateMany',
30
+ 'updateManyAndReturn',
31
+ 'upsert',
32
+ 'delete',
33
+ 'deleteMany',
34
+ ]
35
+
36
+ const CREATED_OPS = new Set([
37
+ 'create',
38
+ 'createMany',
39
+ 'createManyAndReturn',
40
+ ])
41
+
42
+ export function generateHonoHandler(options: {
43
+ model: DMMF.Model
44
+ }): string {
45
+ const modelName = options.model.name
46
+ const prefix = toCamelCase(modelName)
47
+
48
+ const readHandlers = READ_OPS.map((op) => {
49
+ const exportName = `${prefix}${op.charAt(0).toUpperCase() + op.slice(1)}`
50
+
51
+ return `
52
+ export async function ${exportName}(c: Context<HonoEnv>): Promise<void> {
53
+ const data = await core.${coreFnName(op)}(buildContext(c))
54
+ c.set('resultData', data)
55
+ }`
56
+ }).join('\n')
57
+
58
+ const writeHandlers = WRITE_OPS.map((op) => {
59
+ const exportName = `${prefix}${op.charAt(0).toUpperCase() + op.slice(1)}`
60
+ const statusCode = CREATED_OPS.has(op) ? 201 : 200
61
+
62
+ return `
63
+ export async function ${exportName}(c: Context<HonoEnv>): Promise<void> {
64
+ const data = await core.${coreFnName(op)}(buildContext(c))
65
+ c.set('resultData', data)
66
+ c.set('resultStatus', ${statusCode})
67
+ }`
68
+ }).join('\n')
69
+
70
+ return `import type { Context } from 'hono'
71
+ import * as core from './${modelName}Core'
72
+ import type { OperationContext } from '../operationRuntime'
73
+
74
+ type HonoVariables = {
75
+ prisma: any
76
+ postgres?: any
77
+ sqlite?: any
78
+ parsedQuery?: Record<string, unknown>
79
+ body?: unknown
80
+ routeConfig?: any
81
+ guardShape?: Record<string, unknown>
82
+ guardCaller?: string
83
+ resultData?: unknown
84
+ resultStatus?: number
85
+ }
86
+
87
+ type HonoEnv = { Variables: HonoVariables }
88
+
89
+ function buildContext(c: Context<HonoEnv>): OperationContext {
90
+ return {
91
+ prisma: c.get('prisma'),
92
+ postgres: c.get('postgres'),
93
+ sqlite: c.get('sqlite'),
94
+ parsedQuery: c.get('parsedQuery'),
95
+ body: c.get('body'),
96
+ guardShape: c.get('guardShape'),
97
+ guardCaller: c.get('guardCaller'),
98
+ paginationConfig: c.get('routeConfig')?.pagination,
99
+ }
100
+ }
101
+ ${readHandlers}
102
+ ${writeHandlers}
103
+ `
104
+ }
@@ -0,0 +1,380 @@
1
+ import { DMMF } from '@prisma/generator-helper'
2
+ import { toCamelCase } from '../utils/strings'
3
+ import { generateRouteConfigType } from './generateRouteConfigType'
4
+
5
+ export function generateHonoRouterFunction({
6
+ model,
7
+ enums,
8
+ guardShapesImport,
9
+ }: {
10
+ model: DMMF.Model
11
+ enums: DMMF.DatamodelEnum[]
12
+ guardShapesImport: string | null
13
+ }): string {
14
+ const modelName = model.name
15
+ const prefix = toCamelCase(modelName)
16
+ const modelNameLower = modelName.toLowerCase()
17
+ const routerFunctionName = `${prefix}Router`
18
+
19
+ const fieldsMeta = model.fields.map((f) => ({
20
+ name: f.name,
21
+ kind: f.kind,
22
+ type: f.type,
23
+ isList: f.isList,
24
+ isRequired: f.isRequired,
25
+ hasDefaultValue: f.hasDefaultValue,
26
+ isUpdatedAt: f.isUpdatedAt ?? false,
27
+ documentation: f.documentation,
28
+ relationFromFields: f.relationFromFields,
29
+ }))
30
+
31
+ const referencedEnumTypes = new Set(
32
+ model.fields.filter((f) => f.kind === 'enum').map((f) => f.type),
33
+ )
34
+
35
+ const enumsMeta = enums
36
+ .filter((e) => referencedEnumTypes.has(e.name))
37
+ .map((e) => ({
38
+ name: e.name,
39
+ values: e.values.map((v) => ({ name: v.name })),
40
+ }))
41
+
42
+ return `import { Hono } from 'hono'
43
+ import type { Context, Next } from 'hono'
44
+ import { HTTPException } from 'hono/http-exception'
45
+ import {
46
+ ${prefix}FindUnique,
47
+ ${prefix}FindUniqueOrThrow,
48
+ ${prefix}FindFirst,
49
+ ${prefix}FindFirstOrThrow,
50
+ ${prefix}FindMany,
51
+ ${prefix}FindManyPaginated,
52
+ ${prefix}Create,
53
+ ${prefix}CreateMany,
54
+ ${prefix}CreateManyAndReturn,
55
+ ${prefix}Update,
56
+ ${prefix}UpdateMany,
57
+ ${prefix}UpdateManyAndReturn,
58
+ ${prefix}Upsert,
59
+ ${prefix}Delete,
60
+ ${prefix}DeleteMany,
61
+ ${prefix}Aggregate,
62
+ ${prefix}Count,
63
+ ${prefix}GroupBy,
64
+ } from './${modelName}Handlers'
65
+ import type { RouteConfig, HonoHookHandler } from '../routeConfig.target'
66
+ import { parseQueryParams } from '../parseQueryParams'
67
+ import { sanitizeKeys } from '../misc'
68
+ import { buildModelOpenApi } from '../buildModelOpenApi'
69
+ import { mapError, transformResult, HttpError } from '../operationRuntime'
70
+
71
+ ${generateRouteConfigType(modelName, 'HonoHookHandler', guardShapesImport)}
72
+ type HonoVariables = {
73
+ prisma: any
74
+ postgres?: any
75
+ sqlite?: any
76
+ parsedQuery?: Record<string, unknown>
77
+ body?: unknown
78
+ routeConfig?: ${modelName}RouteConfig
79
+ guardShape?: Record<string, unknown>
80
+ guardCaller?: string
81
+ resultData?: unknown
82
+ resultStatus?: number
83
+ }
84
+
85
+ type HonoEnv = { Variables: HonoVariables }
86
+
87
+ const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
88
+
89
+ const MODEL_FIELDS = ${JSON.stringify(fieldsMeta, null, 2)} as const
90
+
91
+ const MODEL_ENUMS = ${JSON.stringify(enumsMeta, null, 2)} as const
92
+
93
+ const defaultOpConfig = {
94
+ before: [] as HonoHookHandler[],
95
+ after: [] as HonoHookHandler[],
96
+ }
97
+
98
+ function normalizePrefix(p: string): string {
99
+ if (!p) return ''
100
+ let result = p
101
+ if (!result.startsWith('/')) result = '/' + result
102
+ while (result.length > 1 && result.endsWith('/')) result = result.slice(0, -1)
103
+ if (result === '/') return ''
104
+ return result
105
+ }
106
+
107
+ async function safeParseBody(c: Context<HonoEnv>): Promise<unknown> {
108
+ try {
109
+ return await c.req.json()
110
+ } catch {
111
+ throw new HttpError(400, 'Invalid JSON in request body')
112
+ }
113
+ }
114
+
115
+ export function ${routerFunctionName}<TCtx = unknown>(
116
+ config: ${modelName}RouteConfig<TCtx> = {},
117
+ ): Hono<HonoEnv> {
118
+ const app = new Hono<HonoEnv>()
119
+
120
+ const customPrefix = normalizePrefix(config.customUrlPrefix || '')
121
+ const modelPrefix = config.addModelPrefix !== false ? '/${modelNameLower}' : ''
122
+ const basePath = customPrefix + modelPrefix
123
+
124
+ const openApiDisabled = config.disableOpenApi === true
125
+ || (config.disableOpenApi !== false && (
126
+ _env.DISABLE_OPENAPI === 'true'
127
+ || _env.NODE_ENV === 'production'
128
+ ))
129
+
130
+ const postReadsEnabled = !config.disablePostReads
131
+
132
+ app.onError((err, c) => {
133
+ if (err instanceof HTTPException) {
134
+ return c.json({ message: err.message }, err.status)
135
+ }
136
+ const httpError = mapError(err)
137
+ return c.json({ message: httpError.message }, httpError.status as any)
138
+ })
139
+
140
+ const parseQueryMw = async (c: Context<HonoEnv>, next: Next): Promise<void> => {
141
+ const raw = c.req.query()
142
+ if (raw && Object.keys(raw).length > 0) {
143
+ c.set(
144
+ 'parsedQuery',
145
+ parseQueryParams(raw as Record<string, unknown>) as Record<string, unknown>,
146
+ )
147
+ }
148
+ await next()
149
+ }
150
+
151
+ const parseBodyAsQueryMw = async (c: Context<HonoEnv>, next: Next): Promise<void> => {
152
+ const body = await safeParseBody(c)
153
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
154
+ throw new HttpError(400, 'Request body must be a JSON object')
155
+ }
156
+ c.set('parsedQuery', sanitizeKeys(body as Record<string, unknown>))
157
+ await next()
158
+ }
159
+
160
+ const parseBodyMw = async (c: Context<HonoEnv>, next: Next): Promise<void> => {
161
+ const body = await safeParseBody(c)
162
+ c.set('body', body)
163
+ await next()
164
+ }
165
+
166
+ const setContextMw = (opConfig: any) => async (c: Context<HonoEnv>, next: Next): Promise<void> => {
167
+ c.set('routeConfig', config as ${modelName}RouteConfig)
168
+ if (opConfig.shape) {
169
+ c.set('guardShape', opConfig.shape)
170
+ const headerName = config.guard?.variantHeader || 'x-api-variant'
171
+ const caller = config.guard?.resolveVariant?.(c as any)
172
+ ?? c.req.header(headerName)
173
+ ?? undefined
174
+ if (caller) {
175
+ c.set('guardCaller', caller)
176
+ }
177
+ }
178
+ await next()
179
+ }
180
+
181
+ const sendResultMw = async (c: Context<HonoEnv>, _next: Next): Promise<Response> => {
182
+ const data = c.get('resultData')
183
+ const status = c.get('resultStatus') ?? 200
184
+ if (data === undefined) {
185
+ return c.json({ message: 'No data set by handler' }, 500)
186
+ }
187
+ return c.json(transformResult(data) as any, status as any)
188
+ }
189
+
190
+ const wrap = (fn: (c: Context<HonoEnv>) => Promise<void>) =>
191
+ async (c: Context<HonoEnv>, next: Next): Promise<void> => {
192
+ await fn(c)
193
+ await next()
194
+ }
195
+
196
+ if (!openApiDisabled) {
197
+ const openapiJsonPath = basePath ? \`\${basePath}/openapi.json\` : '/openapi.json'
198
+ const openapiYamlPath = basePath ? \`\${basePath}/openapi.yaml\` : '/openapi.yaml'
199
+
200
+ app.get(openapiJsonPath, (c) => {
201
+ const spec = buildModelOpenApi(
202
+ '${modelName}',
203
+ MODEL_FIELDS as any,
204
+ MODEL_ENUMS as any,
205
+ config,
206
+ { format: 'json' },
207
+ )
208
+ return c.json(spec as any)
209
+ })
210
+
211
+ app.get(openapiYamlPath, (c) => {
212
+ const yaml = buildModelOpenApi(
213
+ '${modelName}',
214
+ MODEL_FIELDS as any,
215
+ MODEL_ENUMS as any,
216
+ config,
217
+ { format: 'yaml' },
218
+ ) as string
219
+ return c.body(yaml, 200, { 'Content-Type': 'application/yaml' })
220
+ })
221
+ }
222
+
223
+ if (config.enableAll || config.findFirst) {
224
+ const opConfig = config.findFirst || defaultOpConfig
225
+ const { before = [], after = [] } = opConfig
226
+ const path = basePath ? \`\${basePath}/first\` : '/first'
227
+ app.get(path, parseQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindFirst), ...after, sendResultMw)
228
+ if (postReadsEnabled) {
229
+ app.post(path, parseBodyAsQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindFirst), ...after, sendResultMw)
230
+ }
231
+ }
232
+
233
+ if (config.enableAll || config.findFirstOrThrow) {
234
+ const opConfig = config.findFirstOrThrow || defaultOpConfig
235
+ const { before = [], after = [] } = opConfig
236
+ const path = basePath ? \`\${basePath}/first/strict\` : '/first/strict'
237
+ app.get(path, parseQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindFirstOrThrow), ...after, sendResultMw)
238
+ if (postReadsEnabled) {
239
+ app.post(path, parseBodyAsQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindFirstOrThrow), ...after, sendResultMw)
240
+ }
241
+ }
242
+
243
+ if (config.enableAll || config.findManyPaginated) {
244
+ const opConfig = config.findManyPaginated || defaultOpConfig
245
+ const { before = [], after = [] } = opConfig
246
+ const path = basePath ? \`\${basePath}/paginated\` : '/paginated'
247
+ app.get(path, parseQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindManyPaginated), ...after, sendResultMw)
248
+ if (postReadsEnabled) {
249
+ app.post(path, parseBodyAsQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindManyPaginated), ...after, sendResultMw)
250
+ }
251
+ }
252
+
253
+ if (config.enableAll || config.aggregate) {
254
+ const opConfig = config.aggregate || defaultOpConfig
255
+ const { before = [], after = [] } = opConfig
256
+ const path = basePath ? \`\${basePath}/aggregate\` : '/aggregate'
257
+ app.get(path, parseQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}Aggregate), ...after, sendResultMw)
258
+ if (postReadsEnabled) {
259
+ app.post(path, parseBodyAsQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}Aggregate), ...after, sendResultMw)
260
+ }
261
+ }
262
+
263
+ if (config.enableAll || config.count) {
264
+ const opConfig = config.count || defaultOpConfig
265
+ const { before = [], after = [] } = opConfig
266
+ const path = basePath ? \`\${basePath}/count\` : '/count'
267
+ app.get(path, parseQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}Count), ...after, sendResultMw)
268
+ if (postReadsEnabled) {
269
+ app.post(path, parseBodyAsQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}Count), ...after, sendResultMw)
270
+ }
271
+ }
272
+
273
+ if (config.enableAll || config.groupBy) {
274
+ const opConfig = config.groupBy || defaultOpConfig
275
+ const { before = [], after = [] } = opConfig
276
+ const path = basePath ? \`\${basePath}/groupby\` : '/groupby'
277
+ app.get(path, parseQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}GroupBy), ...after, sendResultMw)
278
+ if (postReadsEnabled) {
279
+ app.post(path, parseBodyAsQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}GroupBy), ...after, sendResultMw)
280
+ }
281
+ }
282
+
283
+ if (config.enableAll || config.findUniqueOrThrow) {
284
+ const opConfig = config.findUniqueOrThrow || defaultOpConfig
285
+ const { before = [], after = [] } = opConfig
286
+ const path = basePath ? \`\${basePath}/unique/strict\` : '/unique/strict'
287
+ app.get(path, parseQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindUniqueOrThrow), ...after, sendResultMw)
288
+ if (postReadsEnabled) {
289
+ app.post(path, parseBodyAsQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindUniqueOrThrow), ...after, sendResultMw)
290
+ }
291
+ }
292
+
293
+ if (config.enableAll || config.findUnique) {
294
+ const opConfig = config.findUnique || defaultOpConfig
295
+ const { before = [], after = [] } = opConfig
296
+ const path = basePath ? \`\${basePath}/unique\` : '/unique'
297
+ app.get(path, parseQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindUnique), ...after, sendResultMw)
298
+ if (postReadsEnabled) {
299
+ app.post(path, parseBodyAsQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindUnique), ...after, sendResultMw)
300
+ }
301
+ }
302
+
303
+ if (config.enableAll || config.findMany) {
304
+ const opConfig = config.findMany || defaultOpConfig
305
+ const { before = [], after = [] } = opConfig
306
+ const path = basePath || '/'
307
+ app.get(path, parseQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindMany), ...after, sendResultMw)
308
+ if (postReadsEnabled) {
309
+ const postPath = basePath ? \`\${basePath}/read\` : '/read'
310
+ app.post(postPath, parseBodyAsQueryMw, setContextMw(opConfig), ...before, wrap(${prefix}FindMany), ...after, sendResultMw)
311
+ }
312
+ }
313
+
314
+ if (config.enableAll || config.createManyAndReturn) {
315
+ const opConfig = config.createManyAndReturn || defaultOpConfig
316
+ const { before = [], after = [] } = opConfig
317
+ const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
318
+ app.post(path, parseBodyMw, setContextMw(opConfig), ...before, wrap(${prefix}CreateManyAndReturn), ...after, sendResultMw)
319
+ }
320
+
321
+ if (config.enableAll || config.createMany) {
322
+ const opConfig = config.createMany || defaultOpConfig
323
+ const { before = [], after = [] } = opConfig
324
+ const path = basePath ? \`\${basePath}/many\` : '/many'
325
+ app.post(path, parseBodyMw, setContextMw(opConfig), ...before, wrap(${prefix}CreateMany), ...after, sendResultMw)
326
+ }
327
+
328
+ if (config.enableAll || config.create) {
329
+ const opConfig = config.create || defaultOpConfig
330
+ const { before = [], after = [] } = opConfig
331
+ const path = basePath || '/'
332
+ app.post(path, parseBodyMw, setContextMw(opConfig), ...before, wrap(${prefix}Create), ...after, sendResultMw)
333
+ }
334
+
335
+ if (config.enableAll || config.updateManyAndReturn) {
336
+ const opConfig = config.updateManyAndReturn || defaultOpConfig
337
+ const { before = [], after = [] } = opConfig
338
+ const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
339
+ app.put(path, parseBodyMw, setContextMw(opConfig), ...before, wrap(${prefix}UpdateManyAndReturn), ...after, sendResultMw)
340
+ }
341
+
342
+ if (config.enableAll || config.updateMany) {
343
+ const opConfig = config.updateMany || defaultOpConfig
344
+ const { before = [], after = [] } = opConfig
345
+ const path = basePath ? \`\${basePath}/many\` : '/many'
346
+ app.put(path, parseBodyMw, setContextMw(opConfig), ...before, wrap(${prefix}UpdateMany), ...after, sendResultMw)
347
+ }
348
+
349
+ if (config.enableAll || config.update) {
350
+ const opConfig = config.update || defaultOpConfig
351
+ const { before = [], after = [] } = opConfig
352
+ const path = basePath || '/'
353
+ app.put(path, parseBodyMw, setContextMw(opConfig), ...before, wrap(${prefix}Update), ...after, sendResultMw)
354
+ }
355
+
356
+ if (config.enableAll || config.upsert) {
357
+ const opConfig = config.upsert || defaultOpConfig
358
+ const { before = [], after = [] } = opConfig
359
+ const path = basePath || '/'
360
+ app.patch(path, parseBodyMw, setContextMw(opConfig), ...before, wrap(${prefix}Upsert), ...after, sendResultMw)
361
+ }
362
+
363
+ if (config.enableAll || config.deleteMany) {
364
+ const opConfig = config.deleteMany || defaultOpConfig
365
+ const { before = [], after = [] } = opConfig
366
+ const path = basePath ? \`\${basePath}/many\` : '/many'
367
+ app.delete(path, parseBodyMw, setContextMw(opConfig), ...before, wrap(${prefix}DeleteMany), ...after, sendResultMw)
368
+ }
369
+
370
+ if (config.enableAll || config.delete) {
371
+ const opConfig = config.delete || defaultOpConfig
372
+ const { before = [], after = [] } = opConfig
373
+ const path = basePath || '/'
374
+ app.delete(path, parseBodyMw, setContextMw(opConfig), ...before, wrap(${prefix}Delete), ...after, sendResultMw)
375
+ }
376
+
377
+ return app
378
+ }
379
+ `
380
+ }