prisma-generator-express 1.34.4 → 1.36.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 CHANGED
@@ -11,6 +11,7 @@ Running `npx prisma generate` produces:
11
11
 
12
12
  - Handler functions for all Prisma operations (findMany, create, update, delete, etc.)
13
13
  - Router generator with middleware support (before/after hooks per operation)
14
+ - POST read endpoints for all read operations (for complex queries exceeding URL length limits)
14
15
  - OpenAPI 3.1 spec (JSON and YAML endpoints registered automatically per router)
15
16
  - Documentation helpers for contract view and Scalar UI (require manual mounting)
16
17
  - Client-side query parameter encoder
@@ -29,6 +30,7 @@ Supports both **Express** and **Fastify** targets via the `target` configuration
29
30
  - [Guard shapes (prisma-guard integration)](#guard-shapes-prisma-guard-integration)
30
31
  - [Request body format](#request-body-format)
31
32
  - [Query encoding (client side)](#query-encoding-client-side)
33
+ - [POST read endpoints](#post-read-endpoints)
32
34
  - [Response shaping: select, include, omit](#response-shaping-select-include-omit)
33
35
  - [BigInt and Decimal handling](#bigint-and-decimal-handling)
34
36
  - [Pagination](#pagination)
@@ -551,7 +553,7 @@ const projectConfig = {
551
553
 
552
554
  A request with `{ where: { title: { contains: 'demo' } } }` produces:
553
555
 
554
- ```
556
+ ```sql
555
557
  WHERE status = 'published'
556
558
  AND isDeleted = false
557
559
  AND isActive = true
@@ -995,6 +997,75 @@ const response = await fetch(`/user?${params}`)
995
997
 
996
998
  Complex values (`where`, `select`, `include`, `omit`, `orderBy`) are JSON-stringified. Primitives (`take`, `skip`) are sent directly. The encoder handles BigInt serialization automatically.
997
999
 
1000
+ ## POST read endpoints
1001
+
1002
+ All read operations are available via POST in addition to GET. POST read endpoints accept the same arguments as their GET counterparts, but as a JSON request body instead of query parameters. This is useful when complex filters, deeply nested `where` clauses, or large `select`/`include` objects exceed URL length limits (typically 2048–8192 characters depending on server, proxy, and CDN configuration).
1003
+
1004
+ POST read endpoints are enabled by default. Disable them with `disablePostReads: true` in the route config.
1005
+
1006
+ ### Path mapping
1007
+
1008
+ Most read operations use the same path for both GET and POST. The only exception is `findMany`, which uses a `/read` suffix to avoid conflicting with `POST /` (create).
1009
+
1010
+ | Operation | GET path | POST path |
1011
+ | ----------------- | ---------------- | ---------------- |
1012
+ | findMany | `/{modelName}/` | `/{modelName}/read` |
1013
+ | findFirst | `/{modelName}/first` | `/{modelName}/first` |
1014
+ | findFirstOrThrow | `/{modelName}/first/strict` | `/{modelName}/first/strict` |
1015
+ | findUnique | `/{modelName}/unique` | `/{modelName}/unique` |
1016
+ | findUniqueOrThrow | `/{modelName}/unique/strict` | `/{modelName}/unique/strict` |
1017
+ | findManyPaginated | `/{modelName}/paginated` | `/{modelName}/paginated` |
1018
+ | count | `/{modelName}/count` | `/{modelName}/count` |
1019
+ | aggregate | `/{modelName}/aggregate` | `/{modelName}/aggregate` |
1020
+ | groupBy | `/{modelName}/groupby` | `/{modelName}/groupby` |
1021
+
1022
+ ### Usage
1023
+
1024
+ With GET and `encodeQueryParams`:
1025
+
1026
+ ```ts
1027
+ import { encodeQueryParams } from './generated/client/encodeQueryParams'
1028
+
1029
+ const params = encodeQueryParams({
1030
+ where: { status: 'active', role: { in: ['admin', 'editor'] } },
1031
+ select: { id: true, email: true },
1032
+ take: 20,
1033
+ })
1034
+
1035
+ const response = await fetch(`/user?${params}`)
1036
+ ```
1037
+
1038
+ With POST — same args, no encoding needed:
1039
+
1040
+ ```ts
1041
+ const response = await fetch('/user/read', {
1042
+ method: 'POST',
1043
+ headers: { 'Content-Type': 'application/json' },
1044
+ body: JSON.stringify({
1045
+ where: { status: 'active', role: { in: ['admin', 'editor'] } },
1046
+ select: { id: true, email: true },
1047
+ take: 20,
1048
+ }),
1049
+ })
1050
+ ```
1051
+
1052
+ ### Differences from GET
1053
+
1054
+ POST read bodies use native JSON types directly — numbers are numbers, booleans are booleans, objects are objects. There is no JSON-string encoding of nested values as with GET query parameters, and no string-to-type coercion is applied. The `encodeQueryParams` utility is not needed for POST reads.
1055
+
1056
+ ### Guard shapes
1057
+
1058
+ POST read endpoints use the same guard shapes, hooks, and middleware as their GET counterparts. The same `before`/`after` hooks run for both GET and POST on the same operation.
1059
+
1060
+ ### Disabling
1061
+
1062
+ ```ts
1063
+ app.use('/', UserRouter({
1064
+ enableAll: true,
1065
+ disablePostReads: true,
1066
+ }))
1067
+ ```
1068
+
998
1069
  ## Response shaping: select, include, omit
999
1070
 
1000
1071
  Read and single-record write operations support three response shaping parameters:
@@ -1070,11 +1141,13 @@ Each router automatically registers OpenAPI spec endpoints when not in productio
1070
1141
 
1071
1142
  | Endpoint | Description |
1072
1143
  | ----------------------- | --------------------- |
1073
- | `/{model}/openapi.json` | OpenAPI 3.1 JSON spec |
1074
- | `/{model}/openapi.yaml` | OpenAPI 3.1 YAML spec |
1144
+ | `/{modelName}/openapi.json` | OpenAPI 3.1 JSON spec |
1145
+ | `/{modelName}/openapi.yaml` | OpenAPI 3.1 YAML spec |
1075
1146
 
1076
1147
  Actual paths depend on `customUrlPrefix` and `addModelPrefix` configuration.
1077
1148
 
1149
+ The OpenAPI spec includes POST read endpoints when they are enabled (default). Each POST read operation appears with its own `operationId` and request body schema documenting the native JSON argument types.
1150
+
1078
1151
  ### Manual (generated helpers, require mounting)
1079
1152
 
1080
1153
  The generator produces helper functions that you mount yourself. Pass the same config object used for the router to keep docs and runtime in sync.
@@ -1160,11 +1233,11 @@ fastify.get('/docs', generateCombinedDocs({
1160
1233
  | Endpoint | Description |
1161
1234
  | ----------------------------- | ----------------------- |
1162
1235
  | `/docs` | Combined index page |
1163
- | `/docs/{model}` | Contract view (default) |
1164
- | `/docs/{model}?ui=scalar` | Scalar interactive UI |
1165
- | `/docs/{model}?ui=json` | Raw JSON |
1166
- | `/docs/{model}?ui=yaml` | Raw YAML |
1167
- | `/docs/{model}?ui=playground` | Query playground |
1236
+ | `/docs/{modelName}` | Contract view (default) |
1237
+ | `/docs/{modelName}?ui=scalar` | Scalar interactive UI |
1238
+ | `/docs/{modelName}?ui=json` | Raw JSON |
1239
+ | `/docs/{modelName}?ui=yaml` | Raw YAML |
1240
+ | `/docs/{modelName}?ui=playground` | Query playground |
1168
1241
 
1169
1242
  Disable in production via `NODE_ENV=production` or `DISABLE_OPENAPI=true`. Override with `disableOpenApi: false` in config to force-enable.
1170
1243
 
@@ -1216,31 +1289,44 @@ Without a connector on the request, the handlers use the standard PrismaClient.
1216
1289
 
1217
1290
  GET query values are parsed server-side. Strings starting with `{`, `[`, or `"` are JSON-parsed. The strings `true`, `false`, `null` are converted to their JS equivalents. Numeric conversion applies only to `take` and `skip`, and only when the value is a valid integer (e.g., `"10"` is parsed, `"10.5"` and `""` are not). Use `encodeQueryParams` on the client side to avoid encoding issues.
1218
1291
 
1292
+ POST read endpoints bypass this parsing entirely — the JSON body is used as-is with native types.
1293
+
1219
1294
  ## Router schema
1220
1295
 
1221
- | Operation | Method | Path |
1222
- | ------------------- | ------ | ---------------- |
1223
- | findMany | GET | `/` |
1224
- | findFirst | GET | `/first` |
1225
- | findFirstOrThrow | GET | `/first/strict` |
1226
- | findUnique | GET | `/unique` |
1227
- | findUniqueOrThrow | GET | `/unique/strict` |
1228
- | findManyPaginated | GET | `/paginated` |
1229
- | count | GET | `/count` |
1230
- | aggregate | GET | `/aggregate` |
1231
- | groupBy | GET | `/groupby` |
1232
- | create | POST | `/` |
1233
- | createMany | POST | `/many` |
1234
- | createManyAndReturn | POST | `/many/return` |
1235
- | update | PUT | `/` |
1236
- | updateMany | PUT | `/many` |
1237
- | updateManyAndReturn | PUT | `/many/return` |
1238
- | upsert | PATCH | `/` |
1239
- | delete | DELETE | `/` |
1240
- | deleteMany | DELETE | `/many` |
1296
+ | Operation | Method | Path | Notes |
1297
+ | ------------------- | ------ | ---------------- | ---------------------------------- |
1298
+ | findMany | GET | `/{modelName}/` | |
1299
+ | findMany | POST | `/{modelName}/read` | POST read alternative |
1300
+ | findFirst | GET | `/{modelName}/first` | |
1301
+ | findFirst | POST | `/{modelName}/first` | POST read alternative |
1302
+ | findFirstOrThrow | GET | `/{modelName}/first/strict` | |
1303
+ | findFirstOrThrow | POST | `/{modelName}/first/strict` | POST read alternative |
1304
+ | findUnique | GET | `/{modelName}/unique` | |
1305
+ | findUnique | POST | `/{modelName}/unique` | POST read alternative |
1306
+ | findUniqueOrThrow | GET | `/{modelName}/unique/strict` | |
1307
+ | findUniqueOrThrow | POST | `/{modelName}/unique/strict` | POST read alternative |
1308
+ | findManyPaginated | GET | `/{modelName}/paginated` | |
1309
+ | findManyPaginated | POST | `/{modelName}/paginated` | POST read alternative |
1310
+ | count | GET | `/{modelName}/count` | |
1311
+ | count | POST | `/{modelName}/count` | POST read alternative |
1312
+ | aggregate | GET | `/{modelName}/aggregate` | |
1313
+ | aggregate | POST | `/{modelName}/aggregate` | POST read alternative |
1314
+ | groupBy | GET | `/{modelName}/groupby` | |
1315
+ | groupBy | POST | `/{modelName}/groupby` | POST read alternative |
1316
+ | create | POST | `/{modelName}/` | |
1317
+ | createMany | POST | `/{modelName}/many` | |
1318
+ | createManyAndReturn | POST | `/{modelName}/many/return` | |
1319
+ | update | PUT | `/{modelName}/` | |
1320
+ | updateMany | PUT | `/{modelName}/many` | |
1321
+ | updateManyAndReturn | PUT | `/{modelName}/many/return` | |
1322
+ | upsert | PATCH | `/{modelName}/` | |
1323
+ | delete | DELETE | `/{modelName}/` | |
1324
+ | deleteMany | DELETE | `/{modelName}/many` | |
1241
1325
 
1242
1326
  Paths shown are relative suffixes. Actual paths include the model prefix (e.g., `/user/first`) unless `addModelPrefix: false`, and any `customUrlPrefix`.
1243
1327
 
1328
+ POST read endpoints are enabled by default. Set `disablePostReads: true` to remove them.
1329
+
1244
1330
  ## Skipping models
1245
1331
 
1246
1332
  Add `/// generator off` to a model's documentation to skip generation:
@@ -1263,6 +1349,7 @@ interface RouteConfig {
1263
1349
  customUrlPrefix?: string
1264
1350
  specBasePath?: string
1265
1351
  disableOpenApi?: boolean
1352
+ disablePostReads?: boolean // default: false
1266
1353
  scalarCdnUrl?: string
1267
1354
 
1268
1355
  openApiTitle?: string
@@ -1346,6 +1433,8 @@ The `guard.resolveVariant` callback receives `FastifyRequest` instead of `Reques
1346
1433
 
1347
1434
  `specBasePath` controls the base path used in OpenAPI spec paths and docs examples, independent of `customUrlPrefix`.
1348
1435
 
1436
+ `disablePostReads` removes all POST read endpoints when set to `true`. POST read endpoints are enabled by default. This is a global setting — there is no per-operation toggle.
1437
+
1349
1438
  `openApiServers` sets the `servers` array in the OpenAPI spec:
1350
1439
 
1351
1440
  ```ts
@@ -487,21 +487,29 @@ export async function findManyPaginated(
487
487
  const shape = ctx.guardShape
488
488
  const caller = ctx.guardCaller
489
489
  const distinctCountLimit = ctx.paginationConfig?.distinctCountLimit
490
+ const delegate = (extended as any).${modelNameLower}
490
491
 
491
492
  if (shape) {
492
- assertGuard((extended as any).${modelNameLower})
493
+ assertGuard(delegate)
493
494
  }
494
495
 
495
496
  let items: any[]
496
497
  let total: number
497
498
 
498
- if (typeof extended.$transaction === 'function') {
499
+ if (shape || typeof extended.$transaction !== 'function') {
500
+ const [data, count] = await Promise.all([
501
+ shape
502
+ ? delegate.guard(shape, caller).findMany(query)
503
+ : delegate.findMany(query),
504
+ countForPagination(delegate, query, shape, caller, distinctCountLimit),
505
+ ])
506
+ items = data
507
+ total = count
508
+ } else {
499
509
  try {
500
510
  const txResult = await extended.$transaction(async (tx: any) => {
501
- const d = shape
502
- ? await tx.${modelNameLower}.guard(shape, caller).findMany(query)
503
- : await tx.${modelNameLower}.findMany(query)
504
- const t = await countForPagination(tx.${modelNameLower}, query, shape, caller, distinctCountLimit)
511
+ const d = await tx.${modelNameLower}.findMany(query)
512
+ const t = await countForPagination(tx.${modelNameLower}, query, undefined, undefined, distinctCountLimit)
505
513
  return { d, t }
506
514
  })
507
515
  items = txResult.d
@@ -514,31 +522,12 @@ export async function findManyPaginated(
514
522
  console.warn(
515
523
  '[prisma-generator-express] Interactive transactions not available, pagination queries are non-atomic',
516
524
  )
517
- items = shape
518
- ? await (extended as any).${modelNameLower}.guard(shape, caller).findMany(query)
519
- : await (extended as any).${modelNameLower}.findMany(query)
520
- total = await countForPagination(
521
- (extended as any).${modelNameLower},
522
- query,
523
- shape,
524
- caller,
525
- distinctCountLimit,
526
- )
525
+ items = await delegate.findMany(query)
526
+ total = await countForPagination(delegate, query, undefined, undefined, distinctCountLimit)
527
527
  } else {
528
528
  throw txError
529
529
  }
530
530
  }
531
- } else {
532
- items = shape
533
- ? await (extended as any).${modelNameLower}.guard(shape, caller).findMany(query)
534
- : await (extended as any).${modelNameLower}.findMany(query)
535
- total = await countForPagination(
536
- (extended as any).${modelNameLower},
537
- query,
538
- shape,
539
- caller,
540
- distinctCountLimit,
541
- )
542
531
  }
543
532
 
544
533
  const skip = (query.skip as number) ?? 0
@@ -1 +1 @@
1
- {"version":3,"file":"generateOperationCore.js","sourceRoot":"","sources":["../../src/generators/generateOperationCore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generateOperationCore.js","sourceRoot":"","sources":["../../src/generators/generateOperationCore.ts"],"names":[],"mappings":"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"}
@@ -49,6 +49,7 @@ import {
49
49
  } from './${modelName}Handlers'
50
50
  import type { RouteConfig } from '../routeConfig.target'
51
51
  import { parseQueryParams } from '../parseQueryParams'
52
+ import { sanitizeKeys } from '../misc'
52
53
  import { buildModelOpenApi } from '../buildModelOpenApi'
53
54
  import { transformResult } from '../operationRuntime'
54
55
 
@@ -100,6 +101,8 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
100
101
  || _env.NODE_ENV === 'production'
101
102
  ))
102
103
 
104
+ const postReadsEnabled = !config.disablePostReads
105
+
103
106
  const qbEnabled = isQueryBuilderEnabled(config)
104
107
 
105
108
  if (qbEnabled) {
@@ -117,6 +120,14 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
117
120
  next()
118
121
  }
119
122
 
123
+ const parseBodyAsQuery: RequestHandler = (req, res, next) => {
124
+ if (!req.body || typeof req.body !== 'object' || Array.isArray(req.body)) {
125
+ return next({ status: 400, message: 'Request body must be a JSON object' })
126
+ }
127
+ res.locals.parsedQuery = sanitizeKeys(req.body)
128
+ next()
129
+ }
130
+
120
131
  const setShape = (opConfig: any): RequestHandler => {
121
132
  return (req, res, next) => {
122
133
  res.locals.routeConfig = config
@@ -181,6 +192,9 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
181
192
  const { before = [], after = [] } = opConfig
182
193
  const path = basePath ? \`\${basePath}/first\` : '/first'
183
194
  router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindFirst as RequestHandler, ...after, respond)
195
+ if (postReadsEnabled) {
196
+ router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindFirst as RequestHandler, ...after, respond)
197
+ }
184
198
  }
185
199
 
186
200
  if (config.enableAll || config.findFirstOrThrow) {
@@ -188,6 +202,9 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
188
202
  const { before = [], after = [] } = opConfig
189
203
  const path = basePath ? \`\${basePath}/first/strict\` : '/first/strict'
190
204
  router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindFirstOrThrow as RequestHandler, ...after, respond)
205
+ if (postReadsEnabled) {
206
+ router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindFirstOrThrow as RequestHandler, ...after, respond)
207
+ }
191
208
  }
192
209
 
193
210
  if (config.enableAll || config.findManyPaginated) {
@@ -195,6 +212,9 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
195
212
  const { before = [], after = [] } = opConfig
196
213
  const path = basePath ? \`\${basePath}/paginated\` : '/paginated'
197
214
  router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindManyPaginated as RequestHandler, ...after, respond)
215
+ if (postReadsEnabled) {
216
+ router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindManyPaginated as RequestHandler, ...after, respond)
217
+ }
198
218
  }
199
219
 
200
220
  if (config.enableAll || config.aggregate) {
@@ -202,6 +222,9 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
202
222
  const { before = [], after = [] } = opConfig
203
223
  const path = basePath ? \`\${basePath}/aggregate\` : '/aggregate'
204
224
  router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}Aggregate as RequestHandler, ...after, respond)
225
+ if (postReadsEnabled) {
226
+ router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}Aggregate as RequestHandler, ...after, respond)
227
+ }
205
228
  }
206
229
 
207
230
  if (config.enableAll || config.count) {
@@ -209,6 +232,9 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
209
232
  const { before = [], after = [] } = opConfig
210
233
  const path = basePath ? \`\${basePath}/count\` : '/count'
211
234
  router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}Count as RequestHandler, ...after, respond)
235
+ if (postReadsEnabled) {
236
+ router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}Count as RequestHandler, ...after, respond)
237
+ }
212
238
  }
213
239
 
214
240
  if (config.enableAll || config.groupBy) {
@@ -216,6 +242,9 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
216
242
  const { before = [], after = [] } = opConfig
217
243
  const path = basePath ? \`\${basePath}/groupby\` : '/groupby'
218
244
  router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}GroupBy as RequestHandler, ...after, respond)
245
+ if (postReadsEnabled) {
246
+ router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}GroupBy as RequestHandler, ...after, respond)
247
+ }
219
248
  }
220
249
 
221
250
  if (config.enableAll || config.findUniqueOrThrow) {
@@ -223,6 +252,9 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
223
252
  const { before = [], after = [] } = opConfig
224
253
  const path = basePath ? \`\${basePath}/unique/strict\` : '/unique/strict'
225
254
  router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindUniqueOrThrow as RequestHandler, ...after, respond)
255
+ if (postReadsEnabled) {
256
+ router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindUniqueOrThrow as RequestHandler, ...after, respond)
257
+ }
226
258
  }
227
259
 
228
260
  if (config.enableAll || config.findUnique) {
@@ -230,6 +262,9 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
230
262
  const { before = [], after = [] } = opConfig
231
263
  const path = basePath ? \`\${basePath}/unique\` : '/unique'
232
264
  router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindUnique as RequestHandler, ...after, respond)
265
+ if (postReadsEnabled) {
266
+ router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindUnique as RequestHandler, ...after, respond)
267
+ }
233
268
  }
234
269
 
235
270
  if (config.enableAll || config.findMany) {
@@ -237,6 +272,10 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
237
272
  const { before = [], after = [] } = opConfig
238
273
  const path = basePath || '/'
239
274
  router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindMany as RequestHandler, ...after, respond)
275
+ if (postReadsEnabled) {
276
+ const postPath = basePath ? \`\${basePath}/read\` : '/read'
277
+ router.post(postPath, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindMany as RequestHandler, ...after, respond)
278
+ }
240
279
  }
241
280
 
242
281
  if (config.enableAll || config.createManyAndReturn) {
@@ -1 +1 @@
1
- {"version":3,"file":"generateRouter.js","sourceRoot":"","sources":["../../src/generators/generateRouter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generateRouter.js","sourceRoot":"","sources":["../../src/generators/generateRouter.ts"],"names":[],"mappings":";;AAGA,wDA6WC;AA/WD,8CAA8C;AAE9C,SAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,KAAK,EACL,kBAAkB,GAKnB;IACC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAA;IAC5B,MAAM,MAAM,GAAG,IAAA,qBAAW,EAAC,SAAS,CAAC,CAAA;IACrC,MAAM,cAAc,GAAG,SAAS,CAAC,WAAW,EAAE,CAAA;IAC9C,MAAM,kBAAkB,GAAG,GAAG,MAAM,QAAQ,CAAA;IAE5C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1C,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,eAAe,EAAE,CAAC,CAAC,eAAe;QAClC,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,KAAK;QACnC,aAAa,EAAE,CAAC,CAAC,aAAa;QAC9B,kBAAkB,EAAE,CAAC,CAAC,kBAAkB;KACzC,CAAC,CAAC,CAAA;IAEH,MAAM,mBAAmB,GAAG,IAAI,GAAG,CACjC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CACjE,CAAA;IAED,MAAM,SAAS,GAAG,KAAK;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;SAC9C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;KAChD,CAAC,CAAC,CAAA;IAEL,OAAO;qCAC4B,kBAAkB;;IAEnD,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;YACE,SAAS;;;;;;;;;uBASE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;;sBAEpC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA6BtC,kBAAkB;;;;;;4DAMwB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA0E/D,SAAS;;;;;;;;;;;WAWT,SAAS;;;;;;;;;;;;;;kEAc8C,MAAM;;2EAEG,MAAM;;;;;;;;kEAQf,MAAM;;2EAEG,MAAM;;;;;;;;kEAQf,MAAM;;2EAEG,MAAM;;;;;;;;kEAQf,MAAM;;2EAEG,MAAM;;;;;;;;kEAQf,MAAM;;2EAEG,MAAM;;;;;;;;kEAQf,MAAM;;2EAEG,MAAM;;;;;;;;kEAQf,MAAM;;2EAEG,MAAM;;;;;;;;kEAQf,MAAM;;2EAEG,MAAM;;;;;;;;kEAQf,MAAM;;;+EAGO,MAAM;;;;;;;;uDAQ9B,MAAM;;;;;;;uDAON,MAAM;;;;;;;uDAON,MAAM;;;;;;;sDAOP,MAAM;;;;;;;sDAON,MAAM;;;;;;;sDAON,MAAM;;;;;;;wDAOJ,MAAM;;;;;;;yDAOL,MAAM;;;;;;;yDAON,MAAM;;;;;;;;;;;;;;CAc9D,CAAA;AACD,CAAC"}
@@ -48,8 +48,9 @@ import {
48
48
  } from './${modelName}Handlers'
49
49
  import type { RouteConfig, FastifyHookHandler } from '../routeConfig.target'
50
50
  import { parseQueryParams } from '../parseQueryParams'
51
+ import { sanitizeKeys } from '../misc'
51
52
  import { buildModelOpenApi } from '../buildModelOpenApi'
52
- import { mapError, transformResult } from '../operationRuntime'
53
+ import { mapError, transformResult, HttpError } from '../operationRuntime'
53
54
 
54
55
  const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
55
56
 
@@ -91,6 +92,14 @@ function parseQueryHook(request: FastifyRequest): void {
91
92
  }
92
93
  }
93
94
 
95
+ function parseBodyAsQueryHook(request: FastifyRequest): void {
96
+ const body = request.body
97
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
98
+ throw new HttpError(400, 'Request body must be a JSON object')
99
+ }
100
+ ;(request as any).parsedQuery = sanitizeKeys(body as Record<string, unknown>)
101
+ }
102
+
94
103
  function makeShapeHook(config: RouteConfig, opConfig: any): (request: FastifyRequest) => void {
95
104
  return (request: FastifyRequest) => {
96
105
  ;(request as any).routeConfig = config
@@ -150,6 +159,8 @@ export async function ${routerFunctionName}(
150
159
  || _env.NODE_ENV === 'production'
151
160
  ))
152
161
 
162
+ const postReadsEnabled = !config.disablePostReads
163
+
153
164
  const qbEnabled = isQueryBuilderEnabled(config)
154
165
 
155
166
  if (qbEnabled) {
@@ -210,6 +221,20 @@ export async function ${routerFunctionName}(
210
221
  sendError(reply, error)
211
222
  }
212
223
  })
224
+ if (postReadsEnabled) {
225
+ fastify.post(path, async (request, reply) => {
226
+ try {
227
+ parseBodyAsQueryHook(request)
228
+ makeShapeHook(config, opConfig)(request)
229
+ if (await runHooks(before, request, reply)) return
230
+ await ${prefix}FindFirst(request, reply)
231
+ if (await runHooks(after, request, reply)) return
232
+ sendResult(request, reply)
233
+ } catch (error: unknown) {
234
+ sendError(reply, error)
235
+ }
236
+ })
237
+ }
213
238
  }
214
239
 
215
240
  if (config.enableAll || config.findFirstOrThrow) {
@@ -228,6 +253,20 @@ export async function ${routerFunctionName}(
228
253
  sendError(reply, error)
229
254
  }
230
255
  })
256
+ if (postReadsEnabled) {
257
+ fastify.post(path, async (request, reply) => {
258
+ try {
259
+ parseBodyAsQueryHook(request)
260
+ makeShapeHook(config, opConfig)(request)
261
+ if (await runHooks(before, request, reply)) return
262
+ await ${prefix}FindFirstOrThrow(request, reply)
263
+ if (await runHooks(after, request, reply)) return
264
+ sendResult(request, reply)
265
+ } catch (error: unknown) {
266
+ sendError(reply, error)
267
+ }
268
+ })
269
+ }
231
270
  }
232
271
 
233
272
  if (config.enableAll || config.findManyPaginated) {
@@ -246,6 +285,20 @@ export async function ${routerFunctionName}(
246
285
  sendError(reply, error)
247
286
  }
248
287
  })
288
+ if (postReadsEnabled) {
289
+ fastify.post(path, async (request, reply) => {
290
+ try {
291
+ parseBodyAsQueryHook(request)
292
+ makeShapeHook(config, opConfig)(request)
293
+ if (await runHooks(before, request, reply)) return
294
+ await ${prefix}FindManyPaginated(request, reply)
295
+ if (await runHooks(after, request, reply)) return
296
+ sendResult(request, reply)
297
+ } catch (error: unknown) {
298
+ sendError(reply, error)
299
+ }
300
+ })
301
+ }
249
302
  }
250
303
 
251
304
  if (config.enableAll || config.aggregate) {
@@ -264,6 +317,20 @@ export async function ${routerFunctionName}(
264
317
  sendError(reply, error)
265
318
  }
266
319
  })
320
+ if (postReadsEnabled) {
321
+ fastify.post(path, async (request, reply) => {
322
+ try {
323
+ parseBodyAsQueryHook(request)
324
+ makeShapeHook(config, opConfig)(request)
325
+ if (await runHooks(before, request, reply)) return
326
+ await ${prefix}Aggregate(request, reply)
327
+ if (await runHooks(after, request, reply)) return
328
+ sendResult(request, reply)
329
+ } catch (error: unknown) {
330
+ sendError(reply, error)
331
+ }
332
+ })
333
+ }
267
334
  }
268
335
 
269
336
  if (config.enableAll || config.count) {
@@ -282,6 +349,20 @@ export async function ${routerFunctionName}(
282
349
  sendError(reply, error)
283
350
  }
284
351
  })
352
+ if (postReadsEnabled) {
353
+ fastify.post(path, async (request, reply) => {
354
+ try {
355
+ parseBodyAsQueryHook(request)
356
+ makeShapeHook(config, opConfig)(request)
357
+ if (await runHooks(before, request, reply)) return
358
+ await ${prefix}Count(request, reply)
359
+ if (await runHooks(after, request, reply)) return
360
+ sendResult(request, reply)
361
+ } catch (error: unknown) {
362
+ sendError(reply, error)
363
+ }
364
+ })
365
+ }
285
366
  }
286
367
 
287
368
  if (config.enableAll || config.groupBy) {
@@ -300,6 +381,20 @@ export async function ${routerFunctionName}(
300
381
  sendError(reply, error)
301
382
  }
302
383
  })
384
+ if (postReadsEnabled) {
385
+ fastify.post(path, async (request, reply) => {
386
+ try {
387
+ parseBodyAsQueryHook(request)
388
+ makeShapeHook(config, opConfig)(request)
389
+ if (await runHooks(before, request, reply)) return
390
+ await ${prefix}GroupBy(request, reply)
391
+ if (await runHooks(after, request, reply)) return
392
+ sendResult(request, reply)
393
+ } catch (error: unknown) {
394
+ sendError(reply, error)
395
+ }
396
+ })
397
+ }
303
398
  }
304
399
 
305
400
  if (config.enableAll || config.findUniqueOrThrow) {
@@ -318,6 +413,20 @@ export async function ${routerFunctionName}(
318
413
  sendError(reply, error)
319
414
  }
320
415
  })
416
+ if (postReadsEnabled) {
417
+ fastify.post(path, async (request, reply) => {
418
+ try {
419
+ parseBodyAsQueryHook(request)
420
+ makeShapeHook(config, opConfig)(request)
421
+ if (await runHooks(before, request, reply)) return
422
+ await ${prefix}FindUniqueOrThrow(request, reply)
423
+ if (await runHooks(after, request, reply)) return
424
+ sendResult(request, reply)
425
+ } catch (error: unknown) {
426
+ sendError(reply, error)
427
+ }
428
+ })
429
+ }
321
430
  }
322
431
 
323
432
  if (config.enableAll || config.findUnique) {
@@ -336,6 +445,20 @@ export async function ${routerFunctionName}(
336
445
  sendError(reply, error)
337
446
  }
338
447
  })
448
+ if (postReadsEnabled) {
449
+ fastify.post(path, async (request, reply) => {
450
+ try {
451
+ parseBodyAsQueryHook(request)
452
+ makeShapeHook(config, opConfig)(request)
453
+ if (await runHooks(before, request, reply)) return
454
+ await ${prefix}FindUnique(request, reply)
455
+ if (await runHooks(after, request, reply)) return
456
+ sendResult(request, reply)
457
+ } catch (error: unknown) {
458
+ sendError(reply, error)
459
+ }
460
+ })
461
+ }
339
462
  }
340
463
 
341
464
  if (config.enableAll || config.findMany) {
@@ -354,6 +477,21 @@ export async function ${routerFunctionName}(
354
477
  sendError(reply, error)
355
478
  }
356
479
  })
480
+ if (postReadsEnabled) {
481
+ const postPath = basePath ? \`\${basePath}/read\` : '/read'
482
+ fastify.post(postPath, async (request, reply) => {
483
+ try {
484
+ parseBodyAsQueryHook(request)
485
+ makeShapeHook(config, opConfig)(request)
486
+ if (await runHooks(before, request, reply)) return
487
+ await ${prefix}FindMany(request, reply)
488
+ if (await runHooks(after, request, reply)) return
489
+ sendResult(request, reply)
490
+ } catch (error: unknown) {
491
+ sendError(reply, error)
492
+ }
493
+ })
494
+ }
357
495
  }
358
496
 
359
497
  if (config.enableAll || config.createManyAndReturn) {