prisma-generator-express 1.18.0 → 1.19.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 (71) hide show
  1. package/README.md +399 -194
  2. package/dist/bin.d.ts +2 -0
  3. package/dist/bin.js +1 -1
  4. package/dist/bin.js.map +1 -1
  5. package/dist/client/encodeQueryParams.d.ts +1 -0
  6. package/dist/client/encodeQueryParams.js +33 -0
  7. package/dist/client/encodeQueryParams.js.map +1 -0
  8. package/dist/constants.d.ts +1 -0
  9. package/dist/copy/misc.d.ts +5 -0
  10. package/dist/copy/misc.js +52 -0
  11. package/dist/copy/misc.js.map +1 -0
  12. package/dist/generators/generateImportPrismaStatement.d.ts +3 -0
  13. package/dist/generators/generateImportPrismaStatement.js +55 -0
  14. package/dist/generators/generateImportPrismaStatement.js.map +1 -0
  15. package/dist/generators/generateQueryBuilderHelper.d.ts +2 -0
  16. package/dist/generators/generateQueryBuilderHelper.js +139 -0
  17. package/dist/generators/generateQueryBuilderHelper.js.map +1 -0
  18. package/dist/generators/generateRouter.d.ts +6 -0
  19. package/dist/generators/generateRouter.js +340 -0
  20. package/dist/generators/generateRouter.js.map +1 -0
  21. package/dist/generators/generateUnifiedDocs.d.ts +1 -0
  22. package/dist/generators/generateUnifiedDocs.js +171 -0
  23. package/dist/generators/generateUnifiedDocs.js.map +1 -0
  24. package/dist/generators/generateUnifiedHandler.d.ts +6 -0
  25. package/dist/generators/generateUnifiedHandler.js +444 -0
  26. package/dist/generators/generateUnifiedHandler.js.map +1 -0
  27. package/dist/generators/generateUnifiedScalarUI.d.ts +5 -0
  28. package/dist/generators/generateUnifiedScalarUI.js +1390 -0
  29. package/dist/generators/generateUnifiedScalarUI.js.map +1 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +80 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/utils/copyFiles.d.ts +6 -0
  34. package/dist/utils/copyFiles.js +123 -21
  35. package/dist/utils/copyFiles.js.map +1 -1
  36. package/dist/utils/strings.d.ts +2 -0
  37. package/dist/utils/writeFileSafely.d.ts +10 -0
  38. package/dist/utils/writeFileSafely.js +86 -14
  39. package/dist/utils/writeFileSafely.js.map +1 -1
  40. package/package.json +64 -31
  41. package/src/client/encodeQueryParams.ts +56 -0
  42. package/src/copy/buildModelOpenApi.ts +1569 -0
  43. package/src/copy/misc.ts +21 -0
  44. package/src/copy/operationDefinitions.ts +96 -0
  45. package/src/copy/parseQueryParams.ts +36 -21
  46. package/src/copy/routeConfig.ts +68 -28
  47. package/dist/generator.js +0 -47
  48. package/dist/generator.js.map +0 -1
  49. package/dist/helpers/generateImportPrismaStatement.js +0 -25
  50. package/dist/helpers/generateImportPrismaStatement.js.map +0 -1
  51. package/dist/helpers/generateOperation.js +0 -471
  52. package/dist/helpers/generateOperation.js.map +0 -1
  53. package/dist/helpers/generateRouteFile.js +0 -210
  54. package/dist/helpers/generateRouteFile.js.map +0 -1
  55. package/dist/utils/formatFile.js +0 -26
  56. package/dist/utils/formatFile.js.map +0 -1
  57. package/src/bin.ts +0 -2
  58. package/src/constants.ts +0 -1
  59. package/src/copy/encodeQueryParams.spec.ts +0 -303
  60. package/src/copy/encodeQueryParams.ts +0 -44
  61. package/src/copy/misc.spec.ts +0 -62
  62. package/src/copy/parseQueryParams.spec.ts +0 -187
  63. package/src/copy/transformZod.spec.ts +0 -763
  64. package/src/generator.ts +0 -54
  65. package/src/helpers/generateImportPrismaStatement.ts +0 -38
  66. package/src/helpers/generateOperation.ts +0 -515
  67. package/src/helpers/generateRouteFile.ts +0 -213
  68. package/src/utils/copyFiles.ts +0 -27
  69. package/src/utils/formatFile.ts +0 -22
  70. package/src/utils/strings.ts +0 -7
  71. package/src/utils/writeFileSafely.ts +0 -29
package/README.md CHANGED
@@ -2,292 +2,497 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/prisma-generator-express.svg)](https://badge.fury.io/js/prisma-generator-express)
4
4
  [![npm](https://img.shields.io/npm/dt/prisma-generator-express.svg)](https://www.npmjs.com/package/prisma-generator-express)
5
- [![HitCount](https://hits.dwyl.com/multipliedtwice/prisma-generator-express.svg?style=flat)](http://hits.dwyl.com/multipliedtwice/prisma-generator-express)
6
- [![Coverage Status](https://codecov.io/github/multipliedtwice/prisma-generator-express/graph/badge.svg?token=TTJ30HVKB8)](https://codecov.io/github/multipliedtwice/prisma-generator-express)
5
+ [![Coverage](https://img.shields.io/codecov/c/github/multipliedtwice/prisma-generator-express/main.svg)](https://codecov.io/gh/multipliedtwice/prisma-generator-express)
7
6
  [![npm](https://img.shields.io/npm/l/prisma-generator-express.svg)](LICENSE)
8
7
 
9
- This tool helps you quickly create API endpoints in your Express app using your Prisma models.
8
+ Prisma generator that creates Express CRUD API routes with OpenAPI documentation from your Prisma schema.
10
9
 
11
- Exposes Prisma API to clients - all operations with database is available, including all relationships at any depth.
10
+ Running `npx prisma generate` produces:
12
11
 
13
- When you run `npx prisma generate`, it will:
12
+ - Handler functions for all Prisma operations (findMany, create, update, delete, etc.)
13
+ - Router generator with middleware support (before/after hooks per operation)
14
+ - OpenAPI 3.1 spec (JSON and YAML endpoints registered automatically per router)
15
+ - Documentation helpers for contract view and Scalar UI (require manual mounting)
16
+ - Client-side query parameter encoder
17
+ - Guard/variant shape enforcement via prisma-guard integration
14
18
 
15
- - Generate service (CRUD) functions that you can import into your Express routes.
16
- - Create a router generator function that lets you select which routes to enable in your express app and which middlewares to apply.
19
+ ## Compatibility
17
20
 
18
- ## Table of Contents
21
+ ### Prisma version
19
22
 
20
- - [Installation](#installation)
21
- - [Basic Usage](#basic-usage)
22
- - [Router Generator Usage](#router-generator-usage)
23
- - [Request Object Properties](#request-object-properties)
24
- - [Router Schema](#router-schema)
23
+ Minimum supported Prisma version: **6.0.0**
25
24
 
26
- # Installation
25
+ Some operations require newer versions:
27
26
 
28
- Using npm:
27
+ | Operation | Minimum Prisma version | Notes |
28
+ | --------------------- | ---------------------- | ------------------------------------ |
29
+ | `omit` parameter | 6.2.0 | Returns 400 on versions 6.0.x–6.1.x |
30
+ | `updateManyAndReturn` | 6.2.0 | PostgreSQL, CockroachDB, SQLite only |
29
31
 
32
+ ### Database provider support
33
+
34
+ Most operations work across all Prisma-supported providers. Exceptions:
35
+
36
+ | Feature | PostgreSQL | CockroachDB | MySQL | SQLite | SQL Server | MongoDB |
37
+ | --------------------- | ---------- | ----------- | ----- | ------ | ---------- | ------- |
38
+ | `createManyAndReturn` | ✓ | ✓ | ✗ | ✓ | ✗ | ✗ |
39
+ | `updateManyAndReturn` | ✓ | ✓ | ✗ | ✓ | ✗ | ✗ |
40
+ | `skipDuplicates` | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
41
+
42
+ Operations not supported by your database provider return `501 Not Implemented` at runtime. The generator emits handlers for all operations regardless of provider — use selective route configuration to expose only supported operations.
43
+
44
+ ## Installation
30
45
  ```bash
31
- npm install prisma-generator-express
46
+ npm install -D prisma-generator-express
32
47
  ```
33
48
 
34
- Using yarn:
35
-
49
+ Peer dependencies:
36
50
  ```bash
37
- yarn add prisma-generator-express
51
+ npm install @prisma/client express
38
52
  ```
39
53
 
40
- # Basic usage
54
+ Optional peer dependencies:
55
+ ```bash
56
+ npm install prisma-sql # SQL optimization
57
+ npm install prisma-guard # Guard shape enforcement
58
+ npm install prisma-query-builder-ui # Visual query playground
59
+ ```
41
60
 
42
- - Include this generator in your schema.prisma file:
61
+ ## Setup
43
62
 
63
+ Add the generator to your `schema.prisma`:
44
64
  ```prisma
65
+ generator client {
66
+ provider = "prisma-client-js"
67
+ }
68
+
45
69
  generator express {
46
70
  provider = "prisma-generator-express"
47
71
  }
48
72
  ```
49
73
 
50
- - Generate your middleware functions by running:
74
+ The generator detects the Prisma client generator automatically. All standard provider values are supported: `prisma-client-js`, `@prisma/client`, and `prisma-client`.
51
75
 
76
+ Generate:
52
77
  ```bash
53
- npx prisma generate
78
+ npx prisma generate
54
79
  ```
55
80
 
56
- - Attach Prisma instance
57
-
81
+ ## Usage
58
82
  ```ts
59
- import { PrismaClient } from '@prisma/client'
60
83
  import express from 'express'
84
+ import { PrismaClient } from '@prisma/client'
85
+ import { UserRouter } from './generated/User/UserRouter'
61
86
 
62
87
  const prisma = new PrismaClient()
63
88
  const app = express()
64
89
 
65
- app.use(express.json()) // for parsing application/json
66
-
67
- // Attach Prisma to every request
68
90
  app.use((req, res, next) => {
69
91
  req.prisma = prisma
70
92
  next()
71
93
  })
72
- ```
73
94
 
74
- - Here’s how you can use a generated function in your Express app:
95
+ const userConfig = {
96
+ enableAll: true,
97
+ }
98
+
99
+ app.use('/', UserRouter(userConfig))
100
+
101
+ app.listen(3000, () => {
102
+ console.log('Server is running on http://localhost:3000')
103
+ })
104
+ ```
75
105
 
106
+ ## Selective routes with middleware
76
107
  ```ts
77
- import { UserFindUnique } from './generated/api/UserFindUnique' // Adjust the path as necessary
78
- import { FindUniqueUserSchema } from './prisma-zod-generator/schemas/FindUniqueUser.schema' // Adjust the path as necessary
79
- import { FindUniqueUserSchemaOutput } from './prisma-zod-generator/schemas/FindUniqueUserOutput.schema' // Adjust the path as necessary
80
-
81
- // move this to /helpers
82
- export function validateQuery(schema: ZodSchema) {
83
- return async (req: Request, res: Response, next: NextFunction) => {
84
- try {
85
- req.query = schema.parse(req.query)
86
- next()
87
- } catch (error) {
88
- res.status(400).json({
89
- error: 'Input Validation failed',
90
- details: error.errors,
91
- })
92
- }
93
- }
108
+ const userConfig = {
109
+ findMany: {
110
+ before: [authMiddleware],
111
+ },
112
+ create: {
113
+ before: [authMiddleware, validateBody],
114
+ },
115
+ findUnique: {},
94
116
  }
95
117
 
96
- app.get(
97
- '/user/:id',
98
- validateQuery(FindUniqueUserSchema),
99
- async (req, res, next) => {
100
- // Attach generated Zod schema for output validation
101
- req.outputValidation = FindUniqueUserOutput
102
-
103
- // Use the generated middleware to handle the request
104
- await UserFindUnique(req, res, next)
118
+ app.use('/', UserRouter(userConfig))
119
+ ```
120
+
121
+ Only operations listed in the config (or all when `enableAll: true`) are registered. Operations not listed produce no routes.
122
+
123
+ ## Guard shapes (variant-based field access)
124
+
125
+ Guard shapes require the `prisma-guard` package for runtime enforcement.
126
+
127
+ ### Setup
128
+ ```bash
129
+ npm install prisma-guard
130
+ ```
131
+
132
+ Extend your PrismaClient with the guard extension:
133
+ ```ts
134
+ import { PrismaClient } from '@prisma/client'
135
+ import { guardExtension } from 'prisma-guard'
136
+
137
+ const prisma = new PrismaClient().$extends(guardExtension())
138
+ ```
139
+
140
+ ### Configuration
141
+ ```ts
142
+ const userConfig = {
143
+ findMany: {
144
+ shape: {
145
+ admin: { select: { id: true, email: true, role: true } },
146
+ public: { select: { id: true, email: true } },
147
+ },
105
148
  },
106
- )
149
+ guard: {
150
+ variantHeader: 'x-api-variant',
151
+ },
152
+ }
153
+
154
+ app.use('/', UserRouter(userConfig))
107
155
  ```
108
156
 
109
- The `UserFindUnique` function will fetch the user details from the database, validate the output with Zod, and handle the API response.
157
+ When a guard shape is configured on an operation, the variant is resolved from the configured header (default: `x-api-variant`) or a custom `resolveVariant` function. The resolved variant selects which shape config to apply. If prisma-guard is not installed or the client is not extended with the guard extension, requests to guarded routes return 500 with an actionable error message.
158
+
159
+ ## Request body format
160
+
161
+ All write operations accept the full Prisma args object as the JSON request body. The body must be a JSON object — sending `null`, arrays, or other non-object values returns 400.
162
+ ```ts
163
+ // Create
164
+ { "data": { "name": "Alice", "email": "alice@example.com" }, "select": { "id": true } }
165
+
166
+ // Update
167
+ { "where": { "id": 1 }, "data": { "name": "Bob" } }
168
+
169
+ // Delete
170
+ { "where": { "id": 1 } }
171
+
172
+ // Upsert
173
+ { "where": { "id": 1 }, "create": { "name": "Alice" }, "update": { "name": "Bob" } }
174
+ ```
175
+
176
+ Write operations that return records (create, update, delete, upsert, createManyAndReturn, updateManyAndReturn) support `select`, `include`, and `omit` in the request body to control the response shape.
177
+
178
+ ### Bulk operations
110
179
 
111
- # Router generator usage
180
+ `createMany`, `createManyAndReturn`, `updateMany`, and `updateManyAndReturn` accept scalar-only data inputs. Nested relation writes are not supported in bulk operations.
112
181
 
113
- The library will create functions to generate routers per each model in schema. Each route can accept middleware that will be injected right before generated handler is invoked. You can use it to modify/remove/validate request payload. The output validation can be also attached to `req` object on this step.
182
+ ### Batch operation safety
114
183
 
184
+ `deleteMany`, `updateMany`, and `updateManyAndReturn` require a `where` field in the request body. Requests without `where` are rejected with 400 to prevent accidental mass operations. Sending `{ "where": {} }` is valid and matches all records — this protection catches accidental omission, not intentional broad operations.
185
+
186
+ ## Query encoding (client side)
115
187
  ```ts
116
- import express, { json } from 'express'
117
- import type { Response, Request, NextFunction, RequestHandler } from 'express'
188
+ import { encodeQueryParams } from './generated/client/encodeQueryParams'
118
189
 
119
- import { PrismaClient } from '../prisma/generated/client'
120
- import { UserAccountRouter } from '../prisma/generated/express/UserAccount'
121
- import { RouteConfig } from '~prisma/generated/express/routeConfig'
122
- import { UserAccountFindFirstSchema } from '../prisma/generated/prisma-zod-generator/schemas'
190
+ const params = encodeQueryParams({
191
+ where: { status: 'active', role: { in: ['admin', 'editor'] } },
192
+ select: { id: true, email: true },
193
+ take: 20,
194
+ })
123
195
 
124
- const app = express()
196
+ const response = await fetch(`/user?${params}`)
197
+ ```
125
198
 
126
- const prisma = new PrismaClient()
199
+ Complex values (`where`, `select`, `include`, `omit`, `orderBy`) are JSON-stringified. Primitives (`take`, `skip`) are sent directly. The encoder handles BigInt serialization automatically.
127
200
 
128
- /**
129
- * Middleware to attach Prisma client instance to the request object.
130
- * This ensures that Prisma client is available in all subsequent middleware and route handlers.
131
- */
132
- const addPrisma: RequestHandler = (
133
- req: Request,
134
- res: Response,
135
- next: NextFunction,
136
- ) => {
137
- req.prisma = prisma
138
- next()
139
- }
201
+ ## Response shaping: select, include, omit
140
202
 
141
- /**
142
- * Run context-related operations or modify `req` properties to control the behavior of the route
143
- */
144
- const beforeFindFirst: RequestHandler = (
145
- req: Request,
146
- res: Response,
147
- next: NextFunction,
148
- ) => {
149
- req.passToNext = true
150
- next()
203
+ Read and single-record write operations support three response shaping parameters:
204
+
205
+ - **`select`** — choose which fields to include. Set scalar fields to `true`, use nested objects for relations.
206
+ - **`include`** include relations in addition to all scalar fields. Use nested `include`/`select` for deep loading.
207
+ - **`omit`** — exclude specific scalar fields from the response.
208
+
209
+ `select` and `include` cannot be used together at the same level. `select` and `omit` cannot be used together at the same level. `omit` can be combined with `include`.
210
+
211
+ The `omit` parameter requires Prisma 6.2.0+. On versions 6.0.x–6.1.x, requests using `omit` return 400.
212
+
213
+ ## BigInt and Decimal handling
214
+
215
+ BigInt and Decimal values are serialized as strings in JSON responses. Buffer and Uint8Array values are serialized as base64 strings. The OpenAPI spec documents BigInt and Decimal fields as `type: string`.
216
+
217
+ On the client side, `encodeQueryParams` handles BigInt serialization automatically.
218
+
219
+ ## Pagination
220
+
221
+ `findManyPaginated` returns `{ data, total, hasMore }`. When the runtime supports interactive transactions, the count and query execute in a transaction for consistency. On runtimes without interactive transaction support, the queries run independently with eventual consistency on the `total` count.
222
+
223
+ The `hasMore` field is reliable for forward offset pagination (`skip` + `take`) only. When using cursor-based pagination or negative `take` (backward pagination), `hasMore` may be inaccurate.
224
+
225
+ Configure default and maximum page sizes:
226
+ ```ts
227
+ UserRouter({
228
+ findManyPaginated: {},
229
+ pagination: {
230
+ defaultLimit: 20,
231
+ maxLimit: 100,
232
+ },
233
+ })
234
+ ```
235
+
236
+ `pagination.defaultLimit` is applied when the client omits `take`. `pagination.maxLimit` caps `take` by absolute value. Both settings apply to `findMany` and `findManyPaginated`.
237
+
238
+ ## Error handling
239
+
240
+ All errors are returned as JSON with a `message` field:
241
+ ```json
242
+ { "message": "Unique constraint violation" }
243
+ ```
244
+
245
+ Each generated router installs an error-handling middleware that normalizes errors. Prisma error codes are mapped to appropriate HTTP status codes. Guard errors are mapped as follows: `ShapeError` and `CallerError` → 400, `PolicyError` → 403.
246
+
247
+ | Status | Description |
248
+ | ------ | ------------------------------------------ |
249
+ | 400 | Invalid parameters, body, or query |
250
+ | 403 | Guard policy rejected |
251
+ | 404 | Record not found |
252
+ | 409 | Unique constraint or transaction conflict |
253
+ | 500 | Internal server error |
254
+ | 501 | Feature not supported by database provider |
255
+ | 503 | Database connection pool timeout |
256
+
257
+ ## Security
258
+
259
+ All incoming JSON bodies and query parameters are sanitized to reject `__proto__`, `constructor`, and `prototype` keys, preventing prototype pollution attacks.
260
+
261
+ ## Documentation endpoints
262
+
263
+ ### Automatic (registered by each router)
264
+
265
+ Each router automatically registers OpenAPI spec endpoints when not in production:
266
+
267
+ | Endpoint | Description |
268
+ | ----------------------- | --------------------- |
269
+ | `/{model}/openapi.json` | OpenAPI 3.1 JSON spec |
270
+ | `/{model}/openapi.yaml` | OpenAPI 3.1 YAML spec |
271
+
272
+ Actual paths depend on `customUrlPrefix` and `addModelPrefix` configuration.
273
+
274
+ ### Manual (generated helpers, require mounting)
275
+
276
+ 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:
277
+ ```ts
278
+ import {
279
+ generateCombinedDocs,
280
+ registerModelDocs,
281
+ } from './generated/combinedDocs'
282
+
283
+ const userConfig = {
284
+ findMany: { before: [authMiddleware] },
285
+ create: {},
286
+ findUnique: {},
151
287
  }
152
288
 
153
- /**
154
- * if `req.passToNext` is true, then the result of generated middleware
155
- * will be available in req.locals?.data for modifications
156
- */
157
- const afterFindFirst: RequestHandler = (
158
- req: Request,
159
- res: Response,
160
- next: NextFunction,
161
- ) => {
162
- console.log('req.locals?.data :>> ', req.locals?.data)
163
- next()
289
+ const postConfig = {
290
+ enableAll: true,
164
291
  }
165
292
 
166
- /**
167
- * For generated route the middleware order will be as follows:
168
- * 1. Query parser (kicks in for GET requests)
169
- * 2. Custom middlewares: config.{method}.before[]
170
- * 3. Input validator middleware (Optional): config.{method}.input. For GET request validates `req.query`, for others - `req.body`
171
- * 4. Generated middleware
172
- * 5. Output validator middleware: config.{method}.input
173
- * 6. Custom middlewares: config.{method}.after[] (not available if req.passToNext is falsy)
174
- */
175
- const userAccounRouterConfig: RouteConfig<RequestHandler> = {
176
- findFirst: {
177
- before: [beforeFindFirst],
178
- after: [afterFindFirst],
179
- input: {
180
- schema: UserAccountFindFirstSchema, // make sure you set `isGenerateSelect = true` in prisma-zod-generator
181
- allow: [
182
- 'select.id',
183
- 'select.full_name',
184
- 'select.emailAddress',
185
- 'select.orders[].ProductName',
186
- 'select.orders[].quantity',
187
- 'where.id',
188
- 'where.createdAt',
189
- ],
293
+ app.use('/', UserRouter(userConfig))
294
+ app.use('/', PostRouter(postConfig))
295
+
296
+ registerModelDocs(app, '/docs', {
297
+ User: userConfig,
298
+ Post: postConfig,
299
+ })
300
+
301
+ app.get(
302
+ '/docs',
303
+ generateCombinedDocs({
304
+ title: 'My API',
305
+ modelConfigs: {
306
+ User: userConfig,
307
+ Post: postConfig,
190
308
  },
191
- },
192
- addModelPrefix: true,
309
+ }),
310
+ )
311
+ ```
312
+
313
+ | Endpoint | Description |
314
+ | ----------------------------- | ----------------------- |
315
+ | `/docs` | Combined index page |
316
+ | `/docs/{model}` | Contract view (default) |
317
+ | `/docs/{model}?ui=scalar` | Scalar interactive UI |
318
+ | `/docs/{model}?ui=json` | Raw JSON |
319
+ | `/docs/{model}?ui=yaml` | Raw YAML |
320
+ | `/docs/{model}?ui=playground` | Query playground |
321
+
322
+ Disable in production via `NODE_ENV=production` or `DISABLE_OPENAPI=true`. Override with `disableOpenApi: false` in config to force-enable.
323
+
324
+ ### Spec paths and mount prefixes
325
+
326
+ Use `specBasePath` to set the base path for OpenAPI spec and docs independently of route registration:
327
+ ```ts
328
+ const userConfig = {
193
329
  enableAll: true,
194
- customUrlPrefix: '/v1',
330
+ specBasePath: '/api',
195
331
  }
196
332
 
197
- app.use(addPrisma)
198
- app.use(UserAccountRouter(userAccounRouterConfig))
333
+ app.use('/api', UserRouter(userConfig))
334
+ ```
199
335
 
200
- app.listen(3000, () => {
201
- console.log('Server is running on http://localhost:3000')
336
+ When `specBasePath` is not set, `customUrlPrefix` is used for both runtime routes and spec paths.
337
+
338
+ ## prisma-sql integration
339
+
340
+ When `prisma-sql` is installed, the generated handlers automatically attempt to use its `speedExtension` for optimized SQL execution. The extension activates only when a database connector is provided on the request object.
341
+
342
+ Set `req.postgres` or `req.sqlite` in your middleware to activate the extension:
343
+ ```ts
344
+ import { PrismaClient } from '@prisma/client'
345
+ import postgres from 'postgres'
346
+
347
+ const prisma = new PrismaClient()
348
+ const sql = postgres(process.env.DATABASE_URL!)
349
+
350
+ app.use((req, res, next) => {
351
+ req.prisma = prisma
352
+ req.postgres = sql
353
+ next()
202
354
  })
203
355
  ```
204
356
 
205
- ## Request Object Properties
357
+ Without a connector on the request, the handlers use the standard PrismaClient. Set `DEBUG=true` in the environment to enable prisma-sql debug logging.
358
+
359
+ ## Query parameter parsing
206
360
 
207
- The following properties can be attached to the `req` object to control the behavior of generated middleware:
361
+ 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 only applies to `take` and `skip`. Use `encodeQueryParams` on the client side to avoid encoding issues.
208
362
 
209
- | Property | Type | Description |
210
- | ---------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
211
- | `prisma` | PrismaClient | An instance of PrismaClient that allows the middleware to interact with your database. |
212
- | `passToNext` | boolean | Optional, if `true` - the result of a Prisma request will be passed to the next middleware as `if (req.locals) req.locals.data` |
213
- | `outputValidation` | ZodType | (Optional) A Zod schema used to validate the data returned from the Prisma query before sending it to the client. |
363
+ ## Router schema
214
364
 
215
- ## Router Schema
365
+ | Operation | Method | Path |
366
+ | ------------------- | ------ | ---------------- |
367
+ | findMany | GET | `/` |
368
+ | findFirst | GET | `/first` |
369
+ | findFirstOrThrow | GET | `/first/strict` |
370
+ | findUnique | GET | `/unique` |
371
+ | findUniqueOrThrow | GET | `/unique/strict` |
372
+ | findManyPaginated | GET | `/paginated` |
373
+ | count | GET | `/count` |
374
+ | aggregate | GET | `/aggregate` |
375
+ | groupBy | GET | `/groupby` |
376
+ | create | POST | `/` |
377
+ | createMany | POST | `/many` |
378
+ | createManyAndReturn | POST | `/many/return` |
379
+ | update | PUT | `/` |
380
+ | updateMany | PUT | `/many` |
381
+ | updateManyAndReturn | PUT | `/many/return` |
382
+ | upsert | PATCH | `/` |
383
+ | delete | DELETE | `/` |
384
+ | deleteMany | DELETE | `/many` |
216
385
 
217
- | Function | Method | URL |
218
- | ------------ | -------- | ------------ |
219
- | `findUnique` | `GET` | `/:id` |
220
- | `findFirst` | `GET` | `/first` |
221
- | `findMany` | `GET` | `/` |
222
- | `aggregate` | `GET` | `/aggregate` |
223
- | `count` | `GET` | `/count` |
224
- | `groupBy` | `GET` | `/groupby` |
225
- | `create` | `POST` | `/` |
226
- | `createMany` | `POST` | `/many` |
227
- | `update` | `PUT` | `/` |
228
- | `updateMany` | `PUT` | `/many` |
229
- | `upsert` | `PATCH` | `/` |
230
- | `delete` | `DELETE` | `/` |
231
- | `deleteMany` | `DELETE` | `/many` |
386
+ Paths shown are relative suffixes. Actual paths include the model prefix (e.g., `/user/first`) unless `addModelPrefix: false`, and any `customUrlPrefix`.
232
387
 
233
- ## Skip generation
388
+ ## Skipping models
234
389
 
390
+ Add `/// generator off` to a model's documentation to skip generation:
235
391
  ```prisma
236
392
  /// generator off
237
- model UserAccount {
238
- ID Int @id @default(autoincrement())
239
- full_name String
240
- emailAddress String @unique
241
- createdAt DateTime @default(now())
242
- orders orderItem[]
393
+ model InternalLog {
394
+ id Int @id
243
395
  }
244
396
  ```
245
397
 
246
- ## Helper functions
398
+ ## Configuration
399
+ ```ts
400
+ interface RouteConfig {
401
+ enableAll?: boolean
402
+ addModelPrefix?: boolean // default: true
403
+ customUrlPrefix?: string
404
+ specBasePath?: string
405
+ disableOpenApi?: boolean
406
+ scalarCdnUrl?: string
407
+
408
+ openApiTitle?: string
409
+ openApiDescription?: string
410
+ openApiVersion?: string
411
+ openApiServers?: OpenApiServerConfig[]
412
+ openApiSecuritySchemes?: Record<string, OpenApiSecuritySchemeConfig>
413
+ openApiSecurity?: Record<string, string[]>[]
414
+
415
+ guard?: {
416
+ resolveVariant?: (req: Request) => string | undefined
417
+ variantHeader?: string // default: 'x-api-variant'
418
+ }
247
419
 
248
- ### createValidatorMiddleware(validatorOptions: ValidatorOptions)
420
+ queryBuilder?: QueryBuilderConfig | false
249
421
 
250
- Simple wrapper that internally uses `allow` or `forbid` logic for filtering incoming queries and data payloads. Helps to make sure that schemas from `prisma-zod-generator` is not too permissive.
422
+ pagination?: {
423
+ defaultLimit?: number
424
+ maxLimit?: number
425
+ }
251
426
 
252
- ```ts
253
- interface ValidatorOptions {
254
- schema: ZodSchema<any>
255
- allowedPaths?: string[] // Fobids all except allowed. For example [`where.user.id`, `select.id`], all other provided inputs will throw an error
256
- forbiddenPaths?: string[] // Similar, but allows all, except forbidden
257
- target?: 'body' | 'query'
427
+ // per-operation config
428
+ findMany?: OperationConfig
429
+ findUnique?: OperationConfig
430
+ findUniqueOrThrow?: OperationConfig
431
+ findFirst?: OperationConfig
432
+ findFirstOrThrow?: OperationConfig
433
+ findManyPaginated?: OperationConfig
434
+ create?: OperationConfig
435
+ createMany?: OperationConfig
436
+ createManyAndReturn?: OperationConfig
437
+ update?: OperationConfig
438
+ updateMany?: OperationConfig
439
+ updateManyAndReturn?: OperationConfig
440
+ upsert?: OperationConfig
441
+ delete?: OperationConfig
442
+ deleteMany?: OperationConfig
443
+ aggregate?: OperationConfig
444
+ count?: OperationConfig
445
+ groupBy?: OperationConfig
258
446
  }
259
- ```
260
447
 
261
- ### encodeQueryParams(params: Params)
262
-
263
- It can be used on the frontend to encode Prisma-compatible queries. Alternatively `qs` can be used, but it probably won't work with `OR: [{ blah: false }, { blah: null }]` or some other edge cases.
448
+ interface OperationConfig {
449
+ before?: RequestHandler[]
450
+ after?: RequestHandler[]
451
+ shape?: Record<string, any>
452
+ }
264
453
 
265
- ```ts
266
- type RecursiveUrlParams = {
267
- [key: string]: RecursiveUrlParams | string | boolean | unknown
454
+ interface QueryBuilderConfig {
455
+ enabled?: boolean
456
+ port?: number
457
+ host?: string
458
+ schemaPath?: string
459
+ databaseUrl?: string
268
460
  }
269
- type Params = Record<string, RecursiveUrlParams | string>
270
461
  ```
271
462
 
272
- ### parseQueryParams(params: QueryParams)
463
+ `customUrlPrefix` is normalized to ensure a leading slash and strip trailing slashes.
464
+
465
+ `specBasePath` controls the base path used in OpenAPI spec paths and docs examples, independent of `customUrlPrefix`.
273
466
 
467
+ `openApiServers` sets the `servers` array in the OpenAPI spec:
274
468
  ```ts
275
- type QueryParams = string | ParsedQs | string[] | ParsedQs[] | undefined
469
+ UserRouter({
470
+ enableAll: true,
471
+ openApiServers: [
472
+ { url: 'https://api.example.com/v1', description: 'Production' },
473
+ ],
474
+ })
276
475
  ```
277
476
 
278
- Recursively converts strings "true", "false", "null", and "number" into correct formats.
279
-
280
- ### allow<T extends z.ZodType>( schema: T, allowedPaths: string[] )
281
-
282
- Accepts schema and `['array.of.allowed.paths']`. Throws an error if provided something that doesn't fit allowed schema.
283
-
284
- ### forbid<T extends z.ZodType>( schema: T, forbiddenPaths: string[] )
477
+ `openApiSecuritySchemes` and `openApiSecurity` set the security configuration in the OpenAPI spec:
478
+ ```ts
479
+ UserRouter({
480
+ enableAll: true,
481
+ openApiSecuritySchemes: {
482
+ bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
483
+ },
484
+ openApiSecurity: [{ bearerAuth: [] }],
485
+ })
486
+ ```
285
487
 
286
- Same as `allow` but works in opposite way.
488
+ ## Environment variables
287
489
 
288
- ---
490
+ | Variable | Default | Description |
491
+ | ----------------- | ------- | ----------------------------------- |
492
+ | `DISABLE_OPENAPI` | `false` | Disable OpenAPI endpoints |
493
+ | `NODE_ENV` | - | Set to `production` to disable docs |
494
+ | `DEBUG` | `false` | Enable prisma-sql debug logging |
289
495
 
290
- #### Credits:
291
- - Super Kick Gym - [Brazilian Jiu Jitsu in Bangkok](https://en.bjj-bangkok.com)
496
+ ## License
292
497
 
293
- - Rememo - [Free Task Management and Corporate Chat](https://rememo.io)
498
+ MIT