@pegasusheavy/nestjs-prisma-graphql 1.0.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/LICENSE +201 -0
- package/NOTICE +27 -0
- package/README.md +1235 -0
- package/dist/generate.d.ts +142 -0
- package/dist/generate.js +2567 -0
- package/dist/generate.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +2608 -0
- package/dist/index.js.map +1 -0
- package/package.json +150 -0
package/README.md
ADDED
|
@@ -0,0 +1,1235 @@
|
|
|
1
|
+
# @pegasus-heavy/nestjs-prisma-graphql
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@pegasus-heavy/nestjs-prisma-graphql)
|
|
4
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
5
|
+
[](https://nodejs.org)
|
|
6
|
+
[](https://www.prisma.io)
|
|
7
|
+
[](https://nestjs.com)
|
|
8
|
+
|
|
9
|
+
Generate object types, inputs, args, enums, and more from your Prisma schema for seamless integration with `@nestjs/graphql`.
|
|
10
|
+
|
|
11
|
+
**ESM-first build** — This is an ESM-native fork of [prisma-nestjs-graphql](https://github.com/unlight/prisma-nestjs-graphql), built from the ground up for ESM as the primary module format, with full Prisma 7+ compatibility.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Features](#features)
|
|
18
|
+
- [Requirements](#requirements)
|
|
19
|
+
- [Installation](#installation)
|
|
20
|
+
- [Quick Start](#quick-start)
|
|
21
|
+
- [Generator Options](#generator-options)
|
|
22
|
+
- [Core Options](#core-options)
|
|
23
|
+
- [Code Generation Options](#code-generation-options)
|
|
24
|
+
- [Type Customization Options](#type-customization-options)
|
|
25
|
+
- [Field Decorators](#field-decorators)
|
|
26
|
+
- [@HideField](#hidefield)
|
|
27
|
+
- [@FieldType](#fieldtype)
|
|
28
|
+
- [@PropertyType](#propertytype)
|
|
29
|
+
- [@ObjectType](#objecttype)
|
|
30
|
+
- [@Directive](#directive)
|
|
31
|
+
- [Deprecation](#deprecation)
|
|
32
|
+
- [Complexity](#complexity)
|
|
33
|
+
- [Validation with class-validator](#validation-with-class-validator)
|
|
34
|
+
- [Custom Decorators](#custom-decorators)
|
|
35
|
+
- [GraphQL Scalars](#graphql-scalars)
|
|
36
|
+
- [Built-in Scalar Mappings](#built-in-scalar-mappings)
|
|
37
|
+
- [Custom Scalar Configuration](#custom-scalar-configuration)
|
|
38
|
+
- [ESM Compatibility](#esm-compatibility)
|
|
39
|
+
- [Handling Circular Dependencies](#handling-circular-dependencies)
|
|
40
|
+
- [Type Registry](#type-registry)
|
|
41
|
+
- [Integration Examples](#integration-examples)
|
|
42
|
+
- [Express + Apollo (Default)](#express--apollo-default)
|
|
43
|
+
- [Fastify + Mercurius](#fastify--mercurius)
|
|
44
|
+
- [Fastify + Apollo](#fastify--apollo)
|
|
45
|
+
- [Resolver Example](#resolver-example)
|
|
46
|
+
- [Using with Prisma Service](#using-with-prisma-service)
|
|
47
|
+
- [Generated File Structure](#generated-file-structure)
|
|
48
|
+
- [Troubleshooting](#troubleshooting)
|
|
49
|
+
- [Development](#development)
|
|
50
|
+
- [Contributing](#contributing)
|
|
51
|
+
- [License](#license)
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- 🚀 **ESM-first** — Built with ESM as the primary module format using `lodash-es` and proper `.js` extensions
|
|
58
|
+
- 📦 **Prisma 7+ compatible** — Full support for latest Prisma versions and features
|
|
59
|
+
- 🔄 **Circular dependency handling** — Built-in support for ESM circular import resolution with type registry
|
|
60
|
+
- 🏗️ **NestJS GraphQL ready** — Generates ready-to-use `@nestjs/graphql` decorators and types
|
|
61
|
+
- ⚡ **TypeScript 5.x** — Full support for latest TypeScript features with `NodeNext` module resolution
|
|
62
|
+
- ✅ **class-validator integration** — First-class support for validation decorators
|
|
63
|
+
- 🎯 **Customizable output** — Flexible file patterns, selective generation, and re-export options
|
|
64
|
+
- 🔧 **Extensible** — Custom scalars, decorators, and field type overrides
|
|
65
|
+
- 🏎️ **Express & Fastify** — Works with both Express and Fastify NestJS platforms (Apollo & Mercurius)
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Requirements
|
|
70
|
+
|
|
71
|
+
- **Node.js** >= 20.0.0
|
|
72
|
+
- **Prisma** >= 7.0.0
|
|
73
|
+
- **@nestjs/graphql** >= 12.0.0
|
|
74
|
+
- **TypeScript** >= 5.0.0
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Using pnpm (recommended)
|
|
82
|
+
pnpm add -D @pegasus-heavy/nestjs-prisma-graphql
|
|
83
|
+
|
|
84
|
+
# Using npm
|
|
85
|
+
npm install -D @pegasus-heavy/nestjs-prisma-graphql
|
|
86
|
+
|
|
87
|
+
# Using yarn
|
|
88
|
+
yarn add -D @pegasus-heavy/nestjs-prisma-graphql
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Peer Dependencies
|
|
92
|
+
|
|
93
|
+
Ensure you have the required peer dependencies installed:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pnpm add @nestjs/graphql prisma @prisma/client graphql
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Optional Dependencies
|
|
100
|
+
|
|
101
|
+
For `Decimal` and `Json` field types:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pnpm add graphql-type-json prisma-graphql-type-decimal
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
For validation support:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pnpm add class-validator class-transformer
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Quick Start
|
|
116
|
+
|
|
117
|
+
### 1. Add the Generator to Your Schema
|
|
118
|
+
|
|
119
|
+
```prisma
|
|
120
|
+
// schema.prisma
|
|
121
|
+
datasource db {
|
|
122
|
+
provider = "postgresql"
|
|
123
|
+
url = env("DATABASE_URL")
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
generator client {
|
|
127
|
+
provider = "prisma-client-js"
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
generator nestgraphql {
|
|
131
|
+
provider = "nestjs-prisma-graphql"
|
|
132
|
+
output = "../src/@generated"
|
|
133
|
+
esmCompatible = true
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
model User {
|
|
137
|
+
id String @id @default(cuid())
|
|
138
|
+
email String @unique
|
|
139
|
+
name String?
|
|
140
|
+
posts Post[]
|
|
141
|
+
createdAt DateTime @default(now())
|
|
142
|
+
updatedAt DateTime @updatedAt
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
model Post {
|
|
146
|
+
id String @id @default(cuid())
|
|
147
|
+
title String
|
|
148
|
+
content String?
|
|
149
|
+
published Boolean @default(false)
|
|
150
|
+
author User? @relation(fields: [authorId], references: [id])
|
|
151
|
+
authorId String?
|
|
152
|
+
createdAt DateTime @default(now())
|
|
153
|
+
updatedAt DateTime @updatedAt
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 2. Generate Types
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npx prisma generate
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 3. Use Generated Types
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { Resolver, Query, Args, Mutation } from '@nestjs/graphql';
|
|
167
|
+
import { User } from './@generated/user/user.model';
|
|
168
|
+
import { FindManyUserArgs } from './@generated/user/find-many-user.args';
|
|
169
|
+
import { UserCreateInput } from './@generated/user/user-create.input';
|
|
170
|
+
|
|
171
|
+
@Resolver(() => User)
|
|
172
|
+
export class UserResolver {
|
|
173
|
+
@Query(() => [User])
|
|
174
|
+
async users(@Args() args: FindManyUserArgs): Promise<User[]> {
|
|
175
|
+
// Your implementation
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@Mutation(() => User)
|
|
179
|
+
async createUser(@Args('data') data: UserCreateInput): Promise<User> {
|
|
180
|
+
// Your implementation
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Generator Options
|
|
188
|
+
|
|
189
|
+
### Core Options
|
|
190
|
+
|
|
191
|
+
| Option | Type | Default | Description |
|
|
192
|
+
|--------|------|---------|-------------|
|
|
193
|
+
| `output` | `string` | *required* | Output folder relative to the schema file |
|
|
194
|
+
| `outputFilePattern` | `string` | `{model}/{name}.{type}.ts` | Pattern for generated file paths |
|
|
195
|
+
| `esmCompatible` | `boolean` | `true` | Enable ESM circular import resolution |
|
|
196
|
+
| `prismaClientImport` | `string` | `@prisma/client` | Custom path to Prisma Client |
|
|
197
|
+
| `tsConfigFilePath` | `string` | - | Path to tsconfig.json for type checking |
|
|
198
|
+
| `disabled` | `boolean` | `false` | Disable generation (can also use env vars) |
|
|
199
|
+
|
|
200
|
+
### Disabling the Generator
|
|
201
|
+
|
|
202
|
+
You can disable the generator using environment variables or the `disabled` config option. This is useful for CI environments, build optimization, or conditional generation.
|
|
203
|
+
|
|
204
|
+
#### Environment Variables
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# Disable this specific generator (most specific)
|
|
208
|
+
DISABLE_NESTJS_PRISMA_GRAPHQL=true npx prisma generate
|
|
209
|
+
|
|
210
|
+
# CI-specific skip flag
|
|
211
|
+
CI_SKIP_PRISMA_GRAPHQL=true npx prisma generate
|
|
212
|
+
|
|
213
|
+
# Skip all Prisma generators (common convention)
|
|
214
|
+
PRISMA_GENERATOR_SKIP=true npx prisma generate
|
|
215
|
+
|
|
216
|
+
# Alternative skip flag
|
|
217
|
+
SKIP_PRISMA_GENERATE=true npx prisma generate
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
All environment variables accept `true` or `1` as valid values to disable the generator.
|
|
221
|
+
|
|
222
|
+
#### Config Option
|
|
223
|
+
|
|
224
|
+
```prisma
|
|
225
|
+
generator nestgraphql {
|
|
226
|
+
provider = "nestjs-prisma-graphql"
|
|
227
|
+
output = "../src/@generated"
|
|
228
|
+
disabled = true // Skip generation (accepts: true, "true", "1", "yes")
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### Priority Order
|
|
233
|
+
|
|
234
|
+
1. **Environment variables** are checked first (in order of specificity)
|
|
235
|
+
2. **Config option** is checked if no environment variable disables the generator
|
|
236
|
+
|
|
237
|
+
#### CI/CD Examples
|
|
238
|
+
|
|
239
|
+
```yaml
|
|
240
|
+
# GitHub Actions - skip generation in certain jobs
|
|
241
|
+
- name: Generate Prisma Client Only
|
|
242
|
+
run: npx prisma generate
|
|
243
|
+
env:
|
|
244
|
+
DISABLE_NESTJS_PRISMA_GRAPHQL: true
|
|
245
|
+
|
|
246
|
+
# Docker build - skip during build, generate at runtime
|
|
247
|
+
ARG SKIP_CODEGEN=false
|
|
248
|
+
ENV CI_SKIP_PRISMA_GRAPHQL=$SKIP_CODEGEN
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
When disabled, the generator will output:
|
|
252
|
+
```
|
|
253
|
+
⏭️ nestjs-prisma-graphql: Generation skipped (disabled via environment variable or config)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### Output File Pattern Variables
|
|
257
|
+
|
|
258
|
+
| Variable | Description |
|
|
259
|
+
|----------|-------------|
|
|
260
|
+
| `{model}` | Model name in different cases |
|
|
261
|
+
| `{name}` | Type/class name |
|
|
262
|
+
| `{type}` | File type (model, input, args, enum, output) |
|
|
263
|
+
| `{plural.type}` | Pluralized file type |
|
|
264
|
+
|
|
265
|
+
**Examples:**
|
|
266
|
+
|
|
267
|
+
```prisma
|
|
268
|
+
# Default: src/@generated/user/user-create.input.ts
|
|
269
|
+
outputFilePattern = "{model}/{name}.{type}.ts"
|
|
270
|
+
|
|
271
|
+
# Flat structure: src/@generated/user-create.input.ts
|
|
272
|
+
outputFilePattern = "{name}.{type}.ts"
|
|
273
|
+
|
|
274
|
+
# By type: src/@generated/inputs/user-create.input.ts
|
|
275
|
+
outputFilePattern = "{plural.type}/{name}.{type}.ts"
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Code Generation Options
|
|
279
|
+
|
|
280
|
+
| Option | Type | Default | Description |
|
|
281
|
+
|--------|------|---------|-------------|
|
|
282
|
+
| `combineScalarFilters` | `boolean` | `false` | Combine nested/nullable scalar filters into single types |
|
|
283
|
+
| `noAtomicOperations` | `boolean` | `false` | Remove atomic operation input types (IntFieldUpdateOperationsInput, etc.) |
|
|
284
|
+
| `reExport` | `enum` | `None` | Create index.ts barrel files |
|
|
285
|
+
| `emitSingle` | `boolean` | `false` | Generate all types in a single file |
|
|
286
|
+
| `emitCompiled` | `boolean` | `false` | Emit compiled JavaScript alongside TypeScript |
|
|
287
|
+
| `purgeOutput` | `boolean` | `false` | Delete output folder before generating |
|
|
288
|
+
| `emitBlocks` | `string[]` | all | Selective generation: `enums`, `models`, `inputs`, `outputs`, `args` |
|
|
289
|
+
| `requireSingleFieldsInWhereUniqueInput` | `boolean` | `false` | Make unique fields required in WhereUniqueInput |
|
|
290
|
+
|
|
291
|
+
#### reExport Options
|
|
292
|
+
|
|
293
|
+
| Value | Description |
|
|
294
|
+
|-------|-------------|
|
|
295
|
+
| `None` | No barrel files |
|
|
296
|
+
| `Directories` | Create index.ts in each directory |
|
|
297
|
+
| `Single` | Create single index.ts at output root |
|
|
298
|
+
| `All` | Create index.ts in all directories and root |
|
|
299
|
+
|
|
300
|
+
### Type Customization Options
|
|
301
|
+
|
|
302
|
+
| Option | Type | Default | Description |
|
|
303
|
+
|--------|------|---------|-------------|
|
|
304
|
+
| `noTypeId` | `boolean` | `false` | Use `String` instead of `ID` for @id fields |
|
|
305
|
+
| `omitModelsCount` | `boolean` | `false` | Omit `_count` field from model types |
|
|
306
|
+
| `useInputType` | `string` | - | Pattern for selecting input types |
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Field Decorators
|
|
311
|
+
|
|
312
|
+
Use triple-slash comments (`///`) in your Prisma schema to add decorators and customize generated code.
|
|
313
|
+
|
|
314
|
+
### @HideField
|
|
315
|
+
|
|
316
|
+
Hide fields from the GraphQL schema. Useful for sensitive data like passwords.
|
|
317
|
+
|
|
318
|
+
```prisma
|
|
319
|
+
model User {
|
|
320
|
+
id String @id @default(cuid())
|
|
321
|
+
email String @unique
|
|
322
|
+
|
|
323
|
+
/// @HideField()
|
|
324
|
+
password String
|
|
325
|
+
|
|
326
|
+
/// @HideField({ input: true, output: false })
|
|
327
|
+
internalId String?
|
|
328
|
+
|
|
329
|
+
/// @HideField({ match: '*Password*' })
|
|
330
|
+
tempPassword String?
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Options:**
|
|
335
|
+
|
|
336
|
+
| Option | Type | Description |
|
|
337
|
+
|--------|------|-------------|
|
|
338
|
+
| `input` | `boolean` | Hide from input types |
|
|
339
|
+
| `output` | `boolean` | Hide from output types (default: `true`) |
|
|
340
|
+
| `match` | `string \| string[]` | Glob pattern(s) for field name matching |
|
|
341
|
+
|
|
342
|
+
### @FieldType
|
|
343
|
+
|
|
344
|
+
Override the GraphQL field type.
|
|
345
|
+
|
|
346
|
+
```prisma
|
|
347
|
+
model User {
|
|
348
|
+
/// @FieldType('Scalars.GraphQLEmailAddress')
|
|
349
|
+
email String @unique
|
|
350
|
+
|
|
351
|
+
/// @FieldType({ name: 'GraphQLURL', from: 'graphql-scalars', input: true })
|
|
352
|
+
website String?
|
|
353
|
+
|
|
354
|
+
/// @FieldType({ name: 'CustomType', match: 'input' })
|
|
355
|
+
data Json?
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Options:**
|
|
360
|
+
|
|
361
|
+
| Option | Type | Description |
|
|
362
|
+
|--------|------|-------------|
|
|
363
|
+
| `name` | `string` | Type name (required) |
|
|
364
|
+
| `from` | `string` | Import module specifier |
|
|
365
|
+
| `input` | `boolean` | Apply to input types |
|
|
366
|
+
| `output` | `boolean` | Apply to output types |
|
|
367
|
+
| `match` | `string` | Glob pattern for type name matching |
|
|
368
|
+
|
|
369
|
+
### @PropertyType
|
|
370
|
+
|
|
371
|
+
Override the TypeScript property type.
|
|
372
|
+
|
|
373
|
+
```prisma
|
|
374
|
+
model User {
|
|
375
|
+
/// @PropertyType('Buffer')
|
|
376
|
+
avatar Bytes?
|
|
377
|
+
|
|
378
|
+
/// @PropertyType({ name: 'MyCustomClass', from: './custom-class' })
|
|
379
|
+
metadata Json?
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### @ObjectType
|
|
384
|
+
|
|
385
|
+
Customize the generated `@ObjectType()` decorator.
|
|
386
|
+
|
|
387
|
+
```prisma
|
|
388
|
+
/// @ObjectType({ isAbstract: true })
|
|
389
|
+
model BaseEntity {
|
|
390
|
+
id String @id @default(cuid())
|
|
391
|
+
createdAt DateTime @default(now())
|
|
392
|
+
updatedAt DateTime @updatedAt
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/// @ObjectType('UserProfile')
|
|
396
|
+
model User {
|
|
397
|
+
id String @id
|
|
398
|
+
name String
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### @Directive
|
|
403
|
+
|
|
404
|
+
Add custom GraphQL directives.
|
|
405
|
+
|
|
406
|
+
```prisma
|
|
407
|
+
model User {
|
|
408
|
+
/// @Directive('@auth(requires: ADMIN)')
|
|
409
|
+
adminField String?
|
|
410
|
+
|
|
411
|
+
/// @Directive('@deprecated(reason: "Use newField instead")')
|
|
412
|
+
oldField String?
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Deprecation
|
|
417
|
+
|
|
418
|
+
Mark fields as deprecated.
|
|
419
|
+
|
|
420
|
+
```prisma
|
|
421
|
+
model User {
|
|
422
|
+
/// @deprecated Use 'email' instead
|
|
423
|
+
username String?
|
|
424
|
+
|
|
425
|
+
email String @unique
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Complexity
|
|
430
|
+
|
|
431
|
+
Set field complexity for query cost analysis.
|
|
432
|
+
|
|
433
|
+
```prisma
|
|
434
|
+
model User {
|
|
435
|
+
id String @id
|
|
436
|
+
|
|
437
|
+
/// @complexity 5
|
|
438
|
+
posts Post[]
|
|
439
|
+
|
|
440
|
+
/// @complexity 10
|
|
441
|
+
followers User[]
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## Validation with class-validator
|
|
448
|
+
|
|
449
|
+
First-class support for `class-validator` decorators on generated input types.
|
|
450
|
+
|
|
451
|
+
### Configuration
|
|
452
|
+
|
|
453
|
+
Add the validator configuration to your generator:
|
|
454
|
+
|
|
455
|
+
```prisma
|
|
456
|
+
generator nestgraphql {
|
|
457
|
+
provider = "nestjs-prisma-graphql"
|
|
458
|
+
output = "../src/@generated"
|
|
459
|
+
fields_Validator_from = "class-validator"
|
|
460
|
+
fields_Validator_input = true
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Usage in Schema
|
|
465
|
+
|
|
466
|
+
```prisma
|
|
467
|
+
model User {
|
|
468
|
+
id String @id @default(cuid())
|
|
469
|
+
|
|
470
|
+
/// @Validator.IsEmail()
|
|
471
|
+
/// @Validator.MaxLength(255)
|
|
472
|
+
email String @unique
|
|
473
|
+
|
|
474
|
+
/// @Validator.MinLength(2)
|
|
475
|
+
/// @Validator.MaxLength(100)
|
|
476
|
+
name String
|
|
477
|
+
|
|
478
|
+
/// @Validator.IsOptional()
|
|
479
|
+
/// @Validator.IsUrl()
|
|
480
|
+
website String?
|
|
481
|
+
|
|
482
|
+
/// @Validator.Min(0)
|
|
483
|
+
/// @Validator.Max(150)
|
|
484
|
+
age Int?
|
|
485
|
+
|
|
486
|
+
/// @Validator.IsPhoneNumber('US')
|
|
487
|
+
phone String?
|
|
488
|
+
|
|
489
|
+
/// @Validator.Matches(/^[a-zA-Z0-9_]+$/)
|
|
490
|
+
username String @unique
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Generated Output
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
import { IsEmail, MaxLength, MinLength, IsOptional, IsUrl } from 'class-validator';
|
|
498
|
+
|
|
499
|
+
@InputType()
|
|
500
|
+
export class UserCreateInput {
|
|
501
|
+
@IsEmail()
|
|
502
|
+
@MaxLength(255)
|
|
503
|
+
@Field(() => String)
|
|
504
|
+
email!: string;
|
|
505
|
+
|
|
506
|
+
@MinLength(2)
|
|
507
|
+
@MaxLength(100)
|
|
508
|
+
@Field(() => String)
|
|
509
|
+
name!: string;
|
|
510
|
+
|
|
511
|
+
@IsOptional()
|
|
512
|
+
@IsUrl()
|
|
513
|
+
@Field(() => String, { nullable: true })
|
|
514
|
+
website?: string;
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Available Validators
|
|
519
|
+
|
|
520
|
+
All `class-validator` decorators are supported:
|
|
521
|
+
|
|
522
|
+
**String Validators:**
|
|
523
|
+
- `@Validator.IsEmail()`
|
|
524
|
+
- `@Validator.IsUrl()`
|
|
525
|
+
- `@Validator.IsUUID()`
|
|
526
|
+
- `@Validator.MinLength(n)`
|
|
527
|
+
- `@Validator.MaxLength(n)`
|
|
528
|
+
- `@Validator.Matches(regex)`
|
|
529
|
+
- `@Validator.IsAlpha()`
|
|
530
|
+
- `@Validator.IsAlphanumeric()`
|
|
531
|
+
|
|
532
|
+
**Number Validators:**
|
|
533
|
+
- `@Validator.Min(n)`
|
|
534
|
+
- `@Validator.Max(n)`
|
|
535
|
+
- `@Validator.IsPositive()`
|
|
536
|
+
- `@Validator.IsNegative()`
|
|
537
|
+
- `@Validator.IsInt()`
|
|
538
|
+
|
|
539
|
+
**Type Validators:**
|
|
540
|
+
- `@Validator.IsBoolean()`
|
|
541
|
+
- `@Validator.IsDate()`
|
|
542
|
+
- `@Validator.IsArray()`
|
|
543
|
+
- `@Validator.IsObject()`
|
|
544
|
+
|
|
545
|
+
**General:**
|
|
546
|
+
- `@Validator.IsOptional()`
|
|
547
|
+
- `@Validator.IsNotEmpty()`
|
|
548
|
+
- `@Validator.IsDefined()`
|
|
549
|
+
- `@Validator.ValidateNested()`
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
## Custom Decorators
|
|
554
|
+
|
|
555
|
+
Define custom decorator namespaces for your own decorators or third-party libraries.
|
|
556
|
+
|
|
557
|
+
### Configuration
|
|
558
|
+
|
|
559
|
+
```prisma
|
|
560
|
+
generator nestgraphql {
|
|
561
|
+
provider = "nestjs-prisma-graphql"
|
|
562
|
+
output = "../src/@generated"
|
|
563
|
+
|
|
564
|
+
# class-transformer decorators
|
|
565
|
+
fields_Transform_from = "class-transformer"
|
|
566
|
+
fields_Transform_input = true
|
|
567
|
+
|
|
568
|
+
# Custom decorators
|
|
569
|
+
fields_Custom_from = "./decorators"
|
|
570
|
+
fields_Custom_input = true
|
|
571
|
+
fields_Custom_output = true
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Usage
|
|
576
|
+
|
|
577
|
+
```prisma
|
|
578
|
+
model User {
|
|
579
|
+
/// @Transform.Type(() => Date)
|
|
580
|
+
/// @Transform.Transform(({ value }) => new Date(value))
|
|
581
|
+
birthDate DateTime?
|
|
582
|
+
|
|
583
|
+
/// @Custom.Sanitize()
|
|
584
|
+
bio String?
|
|
585
|
+
}
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
590
|
+
## GraphQL Scalars
|
|
591
|
+
|
|
592
|
+
### Built-in Scalar Mappings
|
|
593
|
+
|
|
594
|
+
| Prisma Type | GraphQL Type | TypeScript Type |
|
|
595
|
+
|-------------|--------------|-----------------|
|
|
596
|
+
| `String` | `String` | `string` |
|
|
597
|
+
| `Int` | `Int` | `number` |
|
|
598
|
+
| `Float` | `Float` | `number` |
|
|
599
|
+
| `Boolean` | `Boolean` | `boolean` |
|
|
600
|
+
| `DateTime` | `Date` | `Date \| string` |
|
|
601
|
+
| `Json` | `GraphQLJSON` | `any` |
|
|
602
|
+
| `Decimal` | `GraphQLDecimal` | `Decimal` |
|
|
603
|
+
| `BigInt` | `BigInt` | `bigint \| number` |
|
|
604
|
+
| `Bytes` | `String` | `Uint8Array` |
|
|
605
|
+
| `@id` fields | `ID` | `string` |
|
|
606
|
+
|
|
607
|
+
### Custom Scalar Configuration
|
|
608
|
+
|
|
609
|
+
Override default scalar mappings:
|
|
610
|
+
|
|
611
|
+
```prisma
|
|
612
|
+
generator nestgraphql {
|
|
613
|
+
provider = "nestjs-prisma-graphql"
|
|
614
|
+
output = "../src/@generated"
|
|
615
|
+
|
|
616
|
+
# Custom DateTime scalar
|
|
617
|
+
graphqlScalars_DateTime_name = "GraphQLISODateTime"
|
|
618
|
+
graphqlScalars_DateTime_specifier = "@nestjs/graphql"
|
|
619
|
+
|
|
620
|
+
# Custom JSON scalar
|
|
621
|
+
graphqlScalars_Json_name = "GraphQLJSONObject"
|
|
622
|
+
graphqlScalars_Json_specifier = "graphql-type-json"
|
|
623
|
+
|
|
624
|
+
# Custom BigInt scalar
|
|
625
|
+
graphqlScalars_BigInt_name = "GraphQLBigInt"
|
|
626
|
+
graphqlScalars_BigInt_specifier = "graphql-scalars"
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### Using graphql-scalars
|
|
631
|
+
|
|
632
|
+
```prisma
|
|
633
|
+
generator nestgraphql {
|
|
634
|
+
provider = "nestjs-prisma-graphql"
|
|
635
|
+
output = "../src/@generated"
|
|
636
|
+
|
|
637
|
+
graphqlScalars_DateTime_name = "GraphQLDateTime"
|
|
638
|
+
graphqlScalars_DateTime_specifier = "graphql-scalars"
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
## ESM Compatibility
|
|
645
|
+
|
|
646
|
+
This generator is built from the ground up for ESM (ECMAScript Modules).
|
|
647
|
+
|
|
648
|
+
### Key ESM Features
|
|
649
|
+
|
|
650
|
+
- Uses `lodash-es` instead of `lodash`
|
|
651
|
+
- All imports include `.js` extensions
|
|
652
|
+
- `"type": "module"` in package.json
|
|
653
|
+
- `NodeNext` module resolution
|
|
654
|
+
- Proper `import type` for type-only imports
|
|
655
|
+
|
|
656
|
+
### ESM vs CommonJS: The Circular Dependency Problem
|
|
657
|
+
|
|
658
|
+
In CommonJS, circular dependencies "just work" because modules receive a partial export object that gets filled in as the module executes. In ESM, imports are "live bindings" that reference the actual exported value—which may be `undefined` if the module hasn't finished initializing.
|
|
659
|
+
|
|
660
|
+
**CommonJS behavior:**
|
|
661
|
+
```javascript
|
|
662
|
+
// user.js imports post.js, post.js imports user.js
|
|
663
|
+
// CJS: Both get a partial object that fills in later ✅
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
**ESM behavior:**
|
|
667
|
+
```javascript
|
|
668
|
+
// user.js imports post.js, post.js imports user.js
|
|
669
|
+
// ESM: One of them gets undefined during initialization ❌
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
This causes bundling errors where CJS would produce valid bundles but ESM complains about broken dependency cycles.
|
|
673
|
+
|
|
674
|
+
### The Solution: esmCompatible Mode
|
|
675
|
+
|
|
676
|
+
Enable `esmCompatible` mode to generate code that handles circular dependencies:
|
|
677
|
+
|
|
678
|
+
```prisma
|
|
679
|
+
generator nestgraphql {
|
|
680
|
+
provider = "nestjs-prisma-graphql"
|
|
681
|
+
output = "../src/@generated"
|
|
682
|
+
esmCompatible = true
|
|
683
|
+
}
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
### Type Registry
|
|
687
|
+
|
|
688
|
+
When `esmCompatible` is enabled, the generator creates:
|
|
689
|
+
|
|
690
|
+
1. **`type-registry.ts`** — Central registry with lazy type resolution helpers
|
|
691
|
+
2. **`register-all-types.ts`** — Registers all types at application startup
|
|
692
|
+
|
|
693
|
+
#### Available Functions
|
|
694
|
+
|
|
695
|
+
| Function | Description |
|
|
696
|
+
|----------|-------------|
|
|
697
|
+
| `registerType(name, type)` | Register a type with the registry |
|
|
698
|
+
| `getType<T>(name)` | Get a registered type (for `@Field` decorators) |
|
|
699
|
+
| `forwardRef<T>(name)` | Create a forward reference with error handling |
|
|
700
|
+
| `lazyType<T>(name)` | Create a lazy type thunk (safest pattern) |
|
|
701
|
+
| `isTypeRegistered(name)` | Check if a type is registered |
|
|
702
|
+
| `validateRegistry(types)` | Validate expected types are registered |
|
|
703
|
+
|
|
704
|
+
#### Setup
|
|
705
|
+
|
|
706
|
+
Import the registry **first** in your application bootstrap:
|
|
707
|
+
|
|
708
|
+
```typescript
|
|
709
|
+
// main.ts - register-all-types MUST be the first import!
|
|
710
|
+
import './@generated/register-all-types.js';
|
|
711
|
+
|
|
712
|
+
import 'reflect-metadata';
|
|
713
|
+
import { NestFactory } from '@nestjs/core';
|
|
714
|
+
import { AppModule } from './app.module.js';
|
|
715
|
+
|
|
716
|
+
async function bootstrap() {
|
|
717
|
+
const app = await NestFactory.create(AppModule);
|
|
718
|
+
await app.listen(3000);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
bootstrap();
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
#### How It Works
|
|
725
|
+
|
|
726
|
+
For circular references, the generator uses lazy type resolution:
|
|
727
|
+
|
|
728
|
+
```typescript
|
|
729
|
+
// Instead of direct import (causes circular dependency)
|
|
730
|
+
import { Post } from '../post/post.model.js';
|
|
731
|
+
|
|
732
|
+
// Uses lazy resolution via type registry
|
|
733
|
+
import { getType } from '../type-registry.js';
|
|
734
|
+
import type { Post } from '../post/post.model.js'; // Type-only import is safe
|
|
735
|
+
|
|
736
|
+
@ObjectType()
|
|
737
|
+
export class User {
|
|
738
|
+
@Field(() => getType('Post'), { nullable: true })
|
|
739
|
+
posts?: Post[];
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Register this type
|
|
743
|
+
registerType('User', User);
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
### Bundler Compatibility
|
|
747
|
+
|
|
748
|
+
The ESM-compatible output works with:
|
|
749
|
+
|
|
750
|
+
| Bundler | Status | Notes |
|
|
751
|
+
|---------|--------|-------|
|
|
752
|
+
| **esbuild** | ✅ | Native ESM support |
|
|
753
|
+
| **Vite** | ✅ | Uses esbuild under the hood |
|
|
754
|
+
| **Rollup** | ✅ | With proper config |
|
|
755
|
+
| **webpack** | ✅ | ESM output mode |
|
|
756
|
+
| **tsx/ts-node** | ✅ | With ESM loader |
|
|
757
|
+
| **Node.js** | ✅ | v20+ with `"type": "module"` |
|
|
758
|
+
|
|
759
|
+
### Debugging Circular Dependencies
|
|
760
|
+
|
|
761
|
+
If you see errors like "Cannot access 'X' before initialization":
|
|
762
|
+
|
|
763
|
+
1. **Check import order**: Ensure `register-all-types.js` is imported first
|
|
764
|
+
2. **Verify registration**: Use `getRegisteredTypes()` to see what's registered
|
|
765
|
+
3. **Validate types**: Use `validateRegistry(['User', 'Post'])` to check expected types
|
|
766
|
+
|
|
767
|
+
```typescript
|
|
768
|
+
import { getRegisteredTypes, validateRegistry } from './@generated/type-registry.js';
|
|
769
|
+
|
|
770
|
+
// Debug: see what's registered
|
|
771
|
+
console.log('Registered types:', getRegisteredTypes());
|
|
772
|
+
|
|
773
|
+
// Validate expected types exist
|
|
774
|
+
validateRegistry(['User', 'Post', 'Comment']);
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
---
|
|
778
|
+
|
|
779
|
+
## Integration Examples
|
|
780
|
+
|
|
781
|
+
This library works with both **Express** (default) and **Fastify** NestJS applications.
|
|
782
|
+
|
|
783
|
+
### Express + Apollo (Default)
|
|
784
|
+
|
|
785
|
+
```typescript
|
|
786
|
+
// app.module.ts
|
|
787
|
+
import { Module } from '@nestjs/common';
|
|
788
|
+
import { GraphQLModule } from '@nestjs/graphql';
|
|
789
|
+
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
|
790
|
+
import { join } from 'path';
|
|
791
|
+
|
|
792
|
+
@Module({
|
|
793
|
+
imports: [
|
|
794
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
795
|
+
driver: ApolloDriver,
|
|
796
|
+
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
|
|
797
|
+
sortSchema: true,
|
|
798
|
+
playground: true,
|
|
799
|
+
}),
|
|
800
|
+
// Your feature modules
|
|
801
|
+
UserModule,
|
|
802
|
+
PostModule,
|
|
803
|
+
],
|
|
804
|
+
})
|
|
805
|
+
export class AppModule {}
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
```typescript
|
|
809
|
+
// main.ts (Express)
|
|
810
|
+
import 'reflect-metadata';
|
|
811
|
+
import './@generated/register-all-types.js';
|
|
812
|
+
|
|
813
|
+
import { NestFactory } from '@nestjs/core';
|
|
814
|
+
import { ValidationPipe } from '@nestjs/common';
|
|
815
|
+
import { AppModule } from './app.module.js';
|
|
816
|
+
|
|
817
|
+
async function bootstrap() {
|
|
818
|
+
const app = await NestFactory.create(AppModule);
|
|
819
|
+
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
|
|
820
|
+
await app.listen(3000);
|
|
821
|
+
}
|
|
822
|
+
bootstrap();
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
### Fastify + Mercurius
|
|
826
|
+
|
|
827
|
+
For better performance, you can use Fastify with Mercurius as the GraphQL adapter:
|
|
828
|
+
|
|
829
|
+
```typescript
|
|
830
|
+
// app.module.ts
|
|
831
|
+
import { Module } from '@nestjs/common';
|
|
832
|
+
import { GraphQLModule } from '@nestjs/graphql';
|
|
833
|
+
import { MercuriusDriver, MercuriusDriverConfig } from '@nestjs/mercurius';
|
|
834
|
+
import { join } from 'path';
|
|
835
|
+
|
|
836
|
+
@Module({
|
|
837
|
+
imports: [
|
|
838
|
+
GraphQLModule.forRoot<MercuriusDriverConfig>({
|
|
839
|
+
driver: MercuriusDriver,
|
|
840
|
+
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
|
|
841
|
+
sortSchema: true,
|
|
842
|
+
graphiql: true,
|
|
843
|
+
}),
|
|
844
|
+
// Your feature modules
|
|
845
|
+
UserModule,
|
|
846
|
+
PostModule,
|
|
847
|
+
],
|
|
848
|
+
})
|
|
849
|
+
export class AppModule {}
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
```typescript
|
|
853
|
+
// main.ts (Fastify)
|
|
854
|
+
import 'reflect-metadata';
|
|
855
|
+
import './@generated/register-all-types.js';
|
|
856
|
+
|
|
857
|
+
import { NestFactory } from '@nestjs/core';
|
|
858
|
+
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
|
|
859
|
+
import { ValidationPipe } from '@nestjs/common';
|
|
860
|
+
import { AppModule } from './app.module.js';
|
|
861
|
+
|
|
862
|
+
async function bootstrap() {
|
|
863
|
+
const app = await NestFactory.create<NestFastifyApplication>(
|
|
864
|
+
AppModule,
|
|
865
|
+
new FastifyAdapter({ logger: true }),
|
|
866
|
+
);
|
|
867
|
+
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
|
|
868
|
+
await app.listen(3000, '0.0.0.0');
|
|
869
|
+
}
|
|
870
|
+
bootstrap();
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### Fastify + Apollo
|
|
874
|
+
|
|
875
|
+
You can also use Apollo Server with Fastify:
|
|
876
|
+
|
|
877
|
+
```typescript
|
|
878
|
+
// app.module.ts
|
|
879
|
+
import { Module } from '@nestjs/common';
|
|
880
|
+
import { GraphQLModule } from '@nestjs/graphql';
|
|
881
|
+
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
|
882
|
+
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
|
|
883
|
+
import { join } from 'path';
|
|
884
|
+
|
|
885
|
+
@Module({
|
|
886
|
+
imports: [
|
|
887
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
888
|
+
driver: ApolloDriver,
|
|
889
|
+
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
|
|
890
|
+
sortSchema: true,
|
|
891
|
+
playground: false,
|
|
892
|
+
plugins: [ApolloServerPluginLandingPageLocalDefault()],
|
|
893
|
+
}),
|
|
894
|
+
UserModule,
|
|
895
|
+
PostModule,
|
|
896
|
+
],
|
|
897
|
+
})
|
|
898
|
+
export class AppModule {}
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
```typescript
|
|
902
|
+
// main.ts (Fastify + Apollo)
|
|
903
|
+
import 'reflect-metadata';
|
|
904
|
+
import './@generated/register-all-types.js';
|
|
905
|
+
|
|
906
|
+
import { NestFactory } from '@nestjs/core';
|
|
907
|
+
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
|
|
908
|
+
import { ValidationPipe } from '@nestjs/common';
|
|
909
|
+
import { AppModule } from './app.module.js';
|
|
910
|
+
|
|
911
|
+
async function bootstrap() {
|
|
912
|
+
const app = await NestFactory.create<NestFastifyApplication>(
|
|
913
|
+
AppModule,
|
|
914
|
+
new FastifyAdapter(),
|
|
915
|
+
);
|
|
916
|
+
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
|
|
917
|
+
await app.listen(3000, '0.0.0.0');
|
|
918
|
+
}
|
|
919
|
+
bootstrap();
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
### Resolver Example
|
|
923
|
+
|
|
924
|
+
```typescript
|
|
925
|
+
// user.resolver.ts
|
|
926
|
+
import { Resolver, Query, Mutation, Args, Int } from '@nestjs/graphql';
|
|
927
|
+
import { User } from './@generated/user/user.model.js';
|
|
928
|
+
import { FindManyUserArgs } from './@generated/user/find-many-user.args.js';
|
|
929
|
+
import { FindUniqueUserArgs } from './@generated/user/find-unique-user.args.js';
|
|
930
|
+
import { UserCreateInput } from './@generated/user/user-create.input.js';
|
|
931
|
+
import { UserUpdateInput } from './@generated/user/user-update.input.js';
|
|
932
|
+
import { UserWhereUniqueInput } from './@generated/user/user-where-unique.input.js';
|
|
933
|
+
import { PrismaService } from './prisma.service.js';
|
|
934
|
+
|
|
935
|
+
@Resolver(() => User)
|
|
936
|
+
export class UserResolver {
|
|
937
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
938
|
+
|
|
939
|
+
@Query(() => [User], { name: 'users' })
|
|
940
|
+
async findMany(@Args() args: FindManyUserArgs): Promise<User[]> {
|
|
941
|
+
return this.prisma.user.findMany(args);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
@Query(() => User, { name: 'user', nullable: true })
|
|
945
|
+
async findUnique(@Args() args: FindUniqueUserArgs): Promise<User | null> {
|
|
946
|
+
return this.prisma.user.findUnique(args);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
@Mutation(() => User)
|
|
950
|
+
async createUser(@Args('data') data: UserCreateInput): Promise<User> {
|
|
951
|
+
return this.prisma.user.create({ data });
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
@Mutation(() => User)
|
|
955
|
+
async updateUser(
|
|
956
|
+
@Args('where') where: UserWhereUniqueInput,
|
|
957
|
+
@Args('data') data: UserUpdateInput,
|
|
958
|
+
): Promise<User> {
|
|
959
|
+
return this.prisma.user.update({ where, data });
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
@Mutation(() => User)
|
|
963
|
+
async deleteUser(@Args('where') where: UserWhereUniqueInput): Promise<User> {
|
|
964
|
+
return this.prisma.user.delete({ where });
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
### Using with Prisma Service
|
|
970
|
+
|
|
971
|
+
```typescript
|
|
972
|
+
// prisma.service.ts
|
|
973
|
+
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
|
974
|
+
import { PrismaClient } from '@prisma/client';
|
|
975
|
+
|
|
976
|
+
@Injectable()
|
|
977
|
+
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
|
978
|
+
async onModuleInit(): Promise<void> {
|
|
979
|
+
await this.$connect();
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
async onModuleDestroy(): Promise<void> {
|
|
983
|
+
await this.$disconnect();
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
### With Validation Pipe
|
|
989
|
+
|
|
990
|
+
```typescript
|
|
991
|
+
// main.ts
|
|
992
|
+
import 'reflect-metadata';
|
|
993
|
+
import './@generated/register-all-types.js';
|
|
994
|
+
|
|
995
|
+
import { NestFactory } from '@nestjs/core';
|
|
996
|
+
import { ValidationPipe } from '@nestjs/common';
|
|
997
|
+
import { AppModule } from './app.module.js';
|
|
998
|
+
|
|
999
|
+
async function bootstrap() {
|
|
1000
|
+
const app = await NestFactory.create(AppModule);
|
|
1001
|
+
|
|
1002
|
+
// Enable class-validator validation
|
|
1003
|
+
app.useGlobalPipes(
|
|
1004
|
+
new ValidationPipe({
|
|
1005
|
+
transform: true,
|
|
1006
|
+
whitelist: true,
|
|
1007
|
+
forbidNonWhitelisted: true,
|
|
1008
|
+
}),
|
|
1009
|
+
);
|
|
1010
|
+
|
|
1011
|
+
await app.listen(3000);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
bootstrap();
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
---
|
|
1018
|
+
|
|
1019
|
+
## Generated File Structure
|
|
1020
|
+
|
|
1021
|
+
With default settings, the generator creates:
|
|
1022
|
+
|
|
1023
|
+
```
|
|
1024
|
+
src/@generated/
|
|
1025
|
+
├── prisma/
|
|
1026
|
+
│ ├── sort-order.enum.ts
|
|
1027
|
+
│ ├── query-mode.enum.ts
|
|
1028
|
+
│ └── ...scalar-filters
|
|
1029
|
+
├── user/
|
|
1030
|
+
│ ├── user.model.ts
|
|
1031
|
+
│ ├── user-create.input.ts
|
|
1032
|
+
│ ├── user-update.input.ts
|
|
1033
|
+
│ ├── user-where.input.ts
|
|
1034
|
+
│ ├── user-where-unique.input.ts
|
|
1035
|
+
│ ├── user-order-by.input.ts
|
|
1036
|
+
│ ├── find-many-user.args.ts
|
|
1037
|
+
│ ├── find-unique-user.args.ts
|
|
1038
|
+
│ ├── create-one-user.args.ts
|
|
1039
|
+
│ ├── update-one-user.args.ts
|
|
1040
|
+
│ ├── delete-one-user.args.ts
|
|
1041
|
+
│ └── ...
|
|
1042
|
+
├── post/
|
|
1043
|
+
│ └── ...
|
|
1044
|
+
├── type-registry.ts # ESM type registry
|
|
1045
|
+
├── register-all-types.ts # Type registration
|
|
1046
|
+
└── index.ts # Barrel export (if reExport enabled)
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
---
|
|
1050
|
+
|
|
1051
|
+
## Troubleshooting
|
|
1052
|
+
|
|
1053
|
+
### Common Issues
|
|
1054
|
+
|
|
1055
|
+
#### "Cannot find module" errors in ESM
|
|
1056
|
+
|
|
1057
|
+
Ensure all imports in your code include `.js` extensions:
|
|
1058
|
+
|
|
1059
|
+
```typescript
|
|
1060
|
+
// ❌ Wrong
|
|
1061
|
+
import { User } from './@generated/user/user.model';
|
|
1062
|
+
|
|
1063
|
+
// ✅ Correct
|
|
1064
|
+
import { User } from './@generated/user/user.model.js';
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
#### Circular dependency warnings
|
|
1068
|
+
|
|
1069
|
+
Enable `esmCompatible` mode and import the type registry:
|
|
1070
|
+
|
|
1071
|
+
```typescript
|
|
1072
|
+
// main.ts - import early
|
|
1073
|
+
import './@generated/register-all-types.js';
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
#### Type mismatch with Prisma Client
|
|
1077
|
+
|
|
1078
|
+
Ensure your generator output is excluded from tsconfig's `include`:
|
|
1079
|
+
|
|
1080
|
+
```json
|
|
1081
|
+
{
|
|
1082
|
+
"compilerOptions": { ... },
|
|
1083
|
+
"include": ["src/**/*.ts"],
|
|
1084
|
+
"exclude": ["node_modules", "src/@generated"]
|
|
1085
|
+
}
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
#### Decimal type not found
|
|
1089
|
+
|
|
1090
|
+
Install the required package:
|
|
1091
|
+
|
|
1092
|
+
```bash
|
|
1093
|
+
pnpm add prisma-graphql-type-decimal decimal.js
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
#### JSON type not found
|
|
1097
|
+
|
|
1098
|
+
Install the required package:
|
|
1099
|
+
|
|
1100
|
+
```bash
|
|
1101
|
+
pnpm add graphql-type-json
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
### Debugging
|
|
1105
|
+
|
|
1106
|
+
Enable verbose logging by setting the `DEBUG` environment variable:
|
|
1107
|
+
|
|
1108
|
+
```bash
|
|
1109
|
+
DEBUG=prisma:generator npx prisma generate
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
---
|
|
1113
|
+
|
|
1114
|
+
## Development
|
|
1115
|
+
|
|
1116
|
+
### Prerequisites
|
|
1117
|
+
|
|
1118
|
+
- Node.js >= 20.0.0
|
|
1119
|
+
- pnpm >= 10.0.0
|
|
1120
|
+
|
|
1121
|
+
### Setup
|
|
1122
|
+
|
|
1123
|
+
```bash
|
|
1124
|
+
# Clone the repository
|
|
1125
|
+
git clone https://github.com/pegasusheavy/nestjs-prisma-graphql.git
|
|
1126
|
+
cd nestjs-prisma-graphql
|
|
1127
|
+
|
|
1128
|
+
# Install dependencies
|
|
1129
|
+
pnpm install
|
|
1130
|
+
|
|
1131
|
+
# Build
|
|
1132
|
+
pnpm build
|
|
1133
|
+
|
|
1134
|
+
# Run tests
|
|
1135
|
+
pnpm test
|
|
1136
|
+
|
|
1137
|
+
# Run tests with coverage
|
|
1138
|
+
pnpm test:cov
|
|
1139
|
+
|
|
1140
|
+
# Type check
|
|
1141
|
+
pnpm typecheck
|
|
1142
|
+
|
|
1143
|
+
# Lint
|
|
1144
|
+
pnpm lint
|
|
1145
|
+
|
|
1146
|
+
# Format
|
|
1147
|
+
pnpm format
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
### Project Structure
|
|
1151
|
+
|
|
1152
|
+
```
|
|
1153
|
+
src/
|
|
1154
|
+
├── handlers/ # Event handlers for code generation
|
|
1155
|
+
├── helpers/ # Utility functions
|
|
1156
|
+
├── generate.ts # Main generation logic
|
|
1157
|
+
├── index.ts # Entry point
|
|
1158
|
+
├── types.ts # Type definitions
|
|
1159
|
+
└── event-names.ts # Event constants
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
### Running Tests
|
|
1163
|
+
|
|
1164
|
+
```bash
|
|
1165
|
+
# Run all tests
|
|
1166
|
+
pnpm test
|
|
1167
|
+
|
|
1168
|
+
# Run tests in watch mode
|
|
1169
|
+
pnpm test:watch
|
|
1170
|
+
|
|
1171
|
+
# Run with coverage
|
|
1172
|
+
pnpm test:cov
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
---
|
|
1176
|
+
|
|
1177
|
+
## Contributing
|
|
1178
|
+
|
|
1179
|
+
Contributions are welcome! Please read our contributing guidelines before submitting a pull request.
|
|
1180
|
+
|
|
1181
|
+
### Commit Convention
|
|
1182
|
+
|
|
1183
|
+
This project uses [Conventional Commits](https://www.conventionalcommits.org/):
|
|
1184
|
+
|
|
1185
|
+
- `feat:` New features
|
|
1186
|
+
- `fix:` Bug fixes
|
|
1187
|
+
- `docs:` Documentation changes
|
|
1188
|
+
- `style:` Code style changes (formatting, semicolons, etc.)
|
|
1189
|
+
- `refactor:` Code refactoring
|
|
1190
|
+
- `perf:` Performance improvements
|
|
1191
|
+
- `test:` Adding or updating tests
|
|
1192
|
+
- `chore:` Maintenance tasks
|
|
1193
|
+
|
|
1194
|
+
### Pull Request Process
|
|
1195
|
+
|
|
1196
|
+
1. Fork the repository
|
|
1197
|
+
2. Create a feature branch (`git checkout -b feat/amazing-feature`)
|
|
1198
|
+
3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
|
|
1199
|
+
4. Push to the branch (`git push origin feat/amazing-feature`)
|
|
1200
|
+
5. Open a Pull Request
|
|
1201
|
+
|
|
1202
|
+
---
|
|
1203
|
+
|
|
1204
|
+
## License
|
|
1205
|
+
|
|
1206
|
+
Copyright 2026 Pegasus Heavy Industries LLC
|
|
1207
|
+
|
|
1208
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
1209
|
+
you may not use this file except in compliance with the License.
|
|
1210
|
+
You may obtain a copy of the License at
|
|
1211
|
+
|
|
1212
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
1213
|
+
|
|
1214
|
+
Unless required by applicable law or agreed to in writing, software
|
|
1215
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
1216
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1217
|
+
See the License for the specific language governing permissions and
|
|
1218
|
+
limitations under the License.
|
|
1219
|
+
|
|
1220
|
+
See the [LICENSE](LICENSE) file for full details.
|
|
1221
|
+
|
|
1222
|
+
---
|
|
1223
|
+
|
|
1224
|
+
## Acknowledgments
|
|
1225
|
+
|
|
1226
|
+
This project is a fork of [prisma-nestjs-graphql](https://github.com/unlight/prisma-nestjs-graphql) by [unlight](https://github.com/unlight). We thank the original author for their excellent work.
|
|
1227
|
+
|
|
1228
|
+
---
|
|
1229
|
+
|
|
1230
|
+
## Support
|
|
1231
|
+
|
|
1232
|
+
- 📖 [Documentation](https://github.com/pegasusheavy/nestjs-prisma-graphql#readme)
|
|
1233
|
+
- 🐛 [Issue Tracker](https://github.com/pegasusheavy/nestjs-prisma-graphql/issues)
|
|
1234
|
+
- 💬 [Discussions](https://github.com/pegasusheavy/nestjs-prisma-graphql/discussions)
|
|
1235
|
+
- ❤️ [Sponsor](https://github.com/sponsors/pegasusheavy)
|