prisma-nestjs-graphql 23.1.0 → 23.2.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
@@ -153,24 +153,24 @@ Default: `''` (empty string)
153
153
 
154
154
  #### `combineScalarFilters`
155
155
 
156
- Combine nested/nullable scalar filters to single
157
- Type: `boolean`
156
+ Combine nested/nullable scalar filters to single
157
+ Type: `boolean`
158
158
  Default: `true`
159
159
 
160
160
  #### `noAtomicOperations`
161
161
 
162
- Remove input types for atomic operations
163
- Type: `boolean`
162
+ Remove input types for atomic operations
163
+ Type: `boolean`
164
164
  Default: `true`
165
165
 
166
166
  #### `reExport`
167
167
 
168
- Create `index.ts` file with re-export
169
- Type: `enum`
170
- Values:
171
- `None` Default, create nothing
172
- `Directories` Create index file in all root directories
173
- `Single` Create single index file in output directory
168
+ Create `index.ts` file with re-export
169
+ Type: `enum`
170
+ Values:
171
+ `None` Default, create nothing
172
+ `Directories` Create index file in all root directories
173
+ `Single` Create single index file in output directory
174
174
  `All` Create index file in all root directories and in output directory
175
175
 
176
176
  Example configuration:
@@ -185,60 +185,60 @@ generator nestgraphql {
185
185
 
186
186
  #### `emitSingle`
187
187
 
188
- Generate single file with merged classes and enums.
189
- Type: `boolean`
188
+ Generate single file with merged classes and enums.
189
+ Type: `boolean`
190
190
  Default: `false`
191
191
 
192
192
  #### `emitCompiled`
193
193
 
194
194
  Emit compiled JavaScript and definitions instead of TypeScript sources.
195
- Type: `boolean`
195
+ Type: `boolean`
196
196
  Default: `false`
197
197
 
198
198
  #### `emitBlocks`
199
199
 
200
- Emit only selected blocks. Be aware, that some blocks do depend on others, e.g. one can't emit `models` without emitting `enums`.
201
- Type: `("args" | "inputs" | "outputs" | "models" | "enums")[]`
200
+ Emit only selected blocks. Be aware, that some blocks do depend on others, e.g. one can't emit `models` without emitting `enums`.
201
+ Type: `("args" | "inputs" | "outputs" | "models" | "enums")[]`
202
202
  Default: `["args", "inputs", "outputs", "models", "enums"]`
203
203
 
204
204
  #### `omitModelsCount`
205
205
 
206
- Omit `_count` field from models.
207
- Type: `boolean`
206
+ Omit `_count` field from models.
207
+ Type: `boolean`
208
208
  Default: `false`
209
209
 
210
210
  #### `purgeOutput`
211
211
 
212
- Delete all files in `output` folder.
213
- Type: `boolean`
212
+ Delete all files in `output` folder.
213
+ Type: `boolean`
214
214
  Default: `false`
215
215
 
216
216
  #### `noTypeId`
217
217
 
218
- Disable usage of graphql `ID` type and use `Int/Float` for fields marked as `@id` in schema.
219
- Type: `boolean`
218
+ Disable usage of graphql `ID` type and use `Int/Float` for fields marked as `@id` in schema.
219
+ Type: `boolean`
220
220
  Default: `false`
221
221
 
222
222
  #### `typeListNullable`
223
223
 
224
224
  Adds `nullable: true` for relation list properties out output models,
225
225
  it makes graphql field looks like `[Type!]`, default `[Type!]!`
226
- Type: `boolean`
226
+ Type: `boolean`
227
227
  Default: `false`
228
228
 
229
229
  #### `requireSingleFieldsInWhereUniqueInput`
230
230
 
231
- When a model `*WhereUniqueInput` class has only a single field, mark that field as **required** (TypeScript) and **not nullable** (GraphQL).
232
- See [#58](https://github.com/unlight/prisma-nestjs-graphql/issues/58) for more details.
233
- Type: `boolean`
234
- Default: `false`
231
+ When a model `*WhereUniqueInput` class has only a single field, mark that field as **required** (TypeScript) and **not nullable** (GraphQL).
232
+ See [#58](https://github.com/unlight/prisma-nestjs-graphql/issues/58) for more details.
233
+ Type: `boolean`
234
+ Default: `false`
235
235
  **Note**: It will break compatiblity between Prisma types and generated classes.
236
236
 
237
237
  #### `unsafeCompatibleWhereUniqueInput`
238
238
 
239
239
  Set TypeScript property type as non optional for all fields in `*WhereUniqueInput` classes.
240
- See [#177](https://github.com/unlight/prisma-nestjs-graphql/issues/177) for more details.
241
- Type: `boolean`
240
+ See [#177](https://github.com/unlight/prisma-nestjs-graphql/issues/177) for more details.
241
+ Type: `boolean`
242
242
  Default: `false`
243
243
 
244
244
  #### `inputType`
@@ -481,7 +481,8 @@ It will affect all inputs and outputs types (including models).
481
481
 
482
482
  #### `customImports`
483
483
 
484
- Allow to declare custom import statements. (Only works with emitSingle = true)
484
+ Allow to declare custom import statements.
485
+ **Note**: Only works with `emitSingle = true`
485
486
 
486
487
  **New (config file)**:
487
488
 
@@ -512,6 +513,55 @@ Where `{key}` any identifier to group values (written in [flatten](https://githu
512
513
  - `customImport_{key}_namespaceImport` - use this name as import namespace
513
514
  - `customImport_{key}_namedImport` - named import (without namespace)
514
515
 
516
+ #### `fieldDecoratorArguments`
517
+
518
+ Override `@Field()` decorator arguments for specific fields. Use this to customize pagination fields (take, skip, cursor) or other generated Args fields that don't come from your Prisma schema.
519
+
520
+ Each rule is evaluated against generated field metadata and applied when the `match` function returns `true`. Multiple matching rules are merged in order.
521
+
522
+ **New (config file)**:
523
+
524
+ ```js
525
+ fieldDecoratorArguments: [
526
+ {
527
+ match: ({ objectName, propertyName }) =>
528
+ objectName.endsWith('Args') && propertyName === 'take',
529
+ decoratorArguments: {
530
+ name: 'first',
531
+ defaultValue: 10,
532
+ description: 'Number of records to return',
533
+ },
534
+ },
535
+ {
536
+ match: ({ objectName, propertyName }) =>
537
+ objectName.endsWith('Args') && propertyName === 'skip',
538
+ decoratorArguments: {
539
+ name: 'offset',
540
+ defaultValue: 0,
541
+ description: 'Number of records to skip',
542
+ },
543
+ },
544
+ ]
545
+ ```
546
+
547
+ **Available `decoratorArguments` options:**
548
+ - `nullable` — Mark field as nullable in GraphQL schema
549
+ - `defaultValue` — Default value for the field
550
+ - `description` — Description shown in GraphQL schema
551
+ - `deprecationReason` — Mark field as deprecated
552
+ - `name` — Custom name for the field in GraphQL schema (TypeScript property name stays the same)
553
+ - `complexity` — Complexity value for query cost analysis
554
+ - `middleware` — Array of field middleware functions
555
+
556
+ **Note:** When using the `name` option to override a field name in GraphQL, ensure you understand Prisma field mapping. For example, if you override the `take` field name to `first`, you must update any Prisma query logic that references the field by its original name. Consider using a mapping helper if doing this across multiple queries.
557
+
558
+ The `match` function receives `FieldInfo` with:
559
+ - `objectName` — Class name (e.g., 'FindManyUserArgs')
560
+ - `propertyName` — Property name (e.g., 'take', 'skip')
561
+ - `propertyType` — TypeScript property type
562
+ - `typeName` — GraphQL/Prisma type name
563
+ - `location` — Prisma field location ('scalar', 'inputObjectTypes', etc.)
564
+
515
565
  ## Documentation and field options
516
566
 
517
567
  Comments with triple slash will projected to typescript code comments
@@ -552,9 +602,9 @@ Special directives in triple slash comments for more precise code generation.
552
602
 
553
603
  #### @HideField()
554
604
 
555
- Removes field from GraphQL schema.
556
- By default (without arguments) field will be decorated for hide only in output types (type in schema).
557
- To hide field in input types add `input: true`.
605
+ Removes field from GraphQL schema.
606
+ By default (without arguments) field will be decorated for hide only in output types (type in schema).
607
+ To hide field in input types add `input: true`.
558
608
  To hide field in specific type you can use glob pattern `match: string | string[]`
559
609
  see [outmatch](https://github.com/axtgr/outmatch#usage) for details.
560
610
 
@@ -569,7 +619,7 @@ export default {
569
619
  };
570
620
  ```
571
621
 
572
- The callback receives `FieldInfo` (`location`, `objectName`, `propertyName`, `propertyType`, `typeName`).
622
+ The callback receives `FieldInfo` (`location`, `objectName`, `propertyName`, `propertyType`, `typeName`).
573
623
  When `shouldHideField` is defined, it overrides `@HideField(...)` settings from field comments and legacy `decorate` rules.
574
624
 
575
625
  Examples:
@@ -633,51 +683,51 @@ generator nestgraphql {
633
683
  }
634
684
  ```
635
685
 
636
- Create configuration map in [flatten](https://github.com/hughsk/flat) style for `{namespace}`.
686
+ Create configuration map in [flatten](https://github.com/hughsk/flat) style for `{namespace}`.
637
687
  Where `{namespace}` is a namespace used in field triple slash comment.
638
688
 
639
689
  ##### `fields_{namespace}_from`
640
690
 
641
- Required. Name of the module, which will be used in import (`class-validator`, `graphql-scalars`, etc.)
691
+ Required. Name of the module, which will be used in import (`class-validator`, `graphql-scalars`, etc.)
642
692
  Type: `string`
643
693
 
644
694
  ##### `fields_{namespace}_input`
645
695
 
646
- Means that it will be applied on input types (classes decorated by `InputType`)
647
- Type: `boolean`
696
+ Means that it will be applied on input types (classes decorated by `InputType`)
697
+ Type: `boolean`
648
698
  Default: `false`
649
699
 
650
700
  ##### `fields_{namespace}_output`
651
701
 
652
702
  Means that it will be applied on output types (classes decorated by `ObjectType`),
653
- including models
654
- Type: `boolean`
703
+ including models
704
+ Type: `boolean`
655
705
  Default: `false`
656
706
 
657
707
  ##### `fields_{namespace}_model`
658
708
 
659
- Means that it will be applied only on model types (classes decorated by `ObjectType`)
660
- Type: `boolean`
709
+ Means that it will be applied only on model types (classes decorated by `ObjectType`)
710
+ Type: `boolean`
661
711
  Default: `false`
662
712
 
663
713
  ##### `fields_{namespace}_defaultImport`
664
714
 
665
- Default import name, if module have no namespace.
666
- Type: `undefined | string | true`
667
- Default: `undefined`
715
+ Default import name, if module have no namespace.
716
+ Type: `undefined | string | true`
717
+ Default: `undefined`
668
718
  If defined as `true` then import name will be same as `{namespace}`
669
719
 
670
720
  ##### `fields_{namespace}_namespaceImport`
671
721
 
672
- Import all as this namespace from module
673
- Type: `undefined | string`
722
+ Import all as this namespace from module
723
+ Type: `undefined | string`
674
724
  Default: Equals to `{namespace}`
675
725
 
676
726
  ##### `fields_{namespace}_namedImport`
677
727
 
678
- If imported module has internal namespace, this allow to generate named import,
679
- imported name will be equal to `{namespace}`, see [example of usage](#propertytype)
680
- Type: `boolean`
728
+ If imported module has internal namespace, this allow to generate named import,
729
+ imported name will be equal to `{namespace}`, see [example of usage](#propertytype)
730
+ Type: `boolean`
681
731
  Default: `false`
682
732
 
683
733
  Custom decorators example:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "prisma-nestjs-graphql",
3
3
  "type": "module",
4
- "version": "23.1.0",
4
+ "version": "23.2.0",
5
5
  "license": "MIT",
6
6
  "description": "Generate object types, inputs, args, etc. from prisma schema file for usage with @nestjs/graphql module",
7
7
  "bin": "bin.mjs",
@@ -67,10 +67,10 @@
67
67
  "@eslint/compat": "^2.1.0",
68
68
  "@eslint/js": "^10.0.1",
69
69
  "@nestjs/apollo": "^13.4.0",
70
- "@nestjs/common": "^11.1.19",
71
- "@nestjs/core": "^11.1.19",
70
+ "@nestjs/common": "^11.1.21",
71
+ "@nestjs/core": "^11.1.21",
72
72
  "@nestjs/graphql": "^13.4.0",
73
- "@nestjs/platform-express": "^11.1.19",
73
+ "@nestjs/platform-express": "^11.1.21",
74
74
  "@paljs/plugins": "^9.0.0",
75
75
  "@poppinss/ts-exec": "^1.4.4",
76
76
  "@prisma/adapter-pg": "^7.8.0",
@@ -82,7 +82,7 @@
82
82
  "@types/flat": "^5.0.5",
83
83
  "@types/graceful-fs": "^4.1.9",
84
84
  "@types/lodash": "^4.17.24",
85
- "@types/node": "^25.6.2",
85
+ "@types/node": "^25.8.0",
86
86
  "@types/pluralize": "^0.0.33",
87
87
  "@types/serialize-javascript": "^5.0.4",
88
88
  "@vitest/coverage-v8": "^4.1.6",
@@ -124,7 +124,7 @@
124
124
  "tslib": "^2.8.1",
125
125
  "type-fest": "^5.6.0",
126
126
  "typescript": "^6.0.3",
127
- "typescript-eslint": "^8.59.2",
127
+ "typescript-eslint": "^8.59.3",
128
128
  "vitest": "^4.1.6"
129
129
  }
130
130
  }
@@ -72,6 +72,17 @@ declare class Configuration {
72
72
  input?: boolean;
73
73
  }): boolean;
74
74
  getDecorators(): Generator<DecoratorItem>;
75
+ /**
76
+ * Get field override arguments for a specific field.
77
+ * Returns merged fieldArguments from all matching overrides.
78
+ */
79
+ getFieldOverride(args: {
80
+ objectName: string;
81
+ propertyName: string;
82
+ propertyType: string;
83
+ location: FieldLocation;
84
+ typeName: string;
85
+ }): Record<string, unknown> | undefined;
75
86
  /**
76
87
  * @deprecated Should be replaced by decorators
77
88
  */
@@ -258,13 +269,87 @@ export type DecoratorItem = {
258
269
  /** Decorator name. Can include namespace, e.g. `Transform.Type`. */
259
270
  name: string;
260
271
  /** Import as a named export. */
261
- namedImport: boolean;
272
+ namedImport?: boolean;
262
273
  /** Import as default export.
263
274
  * Use `true` to import by decorator name. */
264
275
  defaultImport?: string | true;
265
276
  /** Import entire module under this namespace. */
266
277
  namespaceImport?: string;
267
278
  };
279
+ /**
280
+ * Arguments passed to the `@Field()` decorator.
281
+ * Follows NestJS GraphQL's FieldOptions structure to avoid conflicts.
282
+ * These are merged into the generated `@Field()` options object.
283
+ *
284
+ * @see https://docs.nestjs.com/graphql/resolvers-map#field-decorator
285
+ */
286
+ export type FieldDecoratorArguments = {
287
+ /**
288
+ * Custom name for the field in GraphQL schema (different from TypeScript property name).
289
+ *
290
+ * **Important:** When you rename a field (e.g., 'take' → 'first'), you must map the GraphQL
291
+ * argument names back to Prisma field names in your resolver, since Prisma expects the
292
+ * original field names.
293
+ *
294
+ * @example
295
+ * // Option 1: Manual mapping in resolver
296
+ * async findMany(args: FindManyArgs): Promise<Item[]> {
297
+ * const { first, ...restArgs } = args as any;
298
+ * return this.prisma.item.findMany({
299
+ * ...restArgs,
300
+ * ...(first !== undefined && { take: first }),
301
+ * });
302
+ * }
303
+ *
304
+ * @example
305
+ * // Option 2: Use a helper function
306
+ * export function mapGraphQLArgsToPrisma(args: any): any {
307
+ * const { first, ...restArgs } = args;
308
+ * return {
309
+ * ...restArgs,
310
+ * ...(first !== undefined && { take: first }),
311
+ * };
312
+ * }
313
+ *
314
+ * async findMany(args: FindManyArgs): Promise<Item[]> {
315
+ * const prismaArgs = mapGraphQLArgsToPrisma(args);
316
+ * return this.prisma.item.findMany(prismaArgs);
317
+ * }
318
+ * export function mapGraphQLArgsToPrisma(args: any): any {
319
+ * const { first, ...restArgs } = args;
320
+ * return {
321
+ * ...restArgs,
322
+ * ...(first !== undefined && { take: first }),
323
+ * };
324
+ * }
325
+ */
326
+ name?: string;
327
+ /** Description shown in GraphQL schema. */
328
+ description?: string;
329
+ /** Mark field as deprecated with optional reason. */
330
+ deprecationReason?: string;
331
+ /** Complexity for query complexity analysis. */
332
+ complexity?: unknown;
333
+ /** Array of middleware to apply to the field. */
334
+ middleware?: unknown[];
335
+ /** Mark field as nullable in GraphQL schema. */
336
+ nullable?: boolean;
337
+ /** Default value for the field. */
338
+ defaultValue?: unknown;
339
+ };
340
+ /**
341
+ * Rule for overriding `@Field()` decorator arguments on generated fields.
342
+ * Use this to customize pagination fields (take, skip) or other generated Args fields.
343
+ */
344
+ export type FieldDecoratorRule = {
345
+ /** Return `true` to apply this override to the current field. */
346
+ match: (args: FieldInfo) => boolean;
347
+ /**
348
+ * Arguments to merge into the `@Field()` decorator options.
349
+ * These are merged with existing arguments (nullable, etc.).
350
+ */
351
+ decoratorArguments: FieldDecoratorArguments;
352
+ };
268
353
  export type ExternalConfig = Partial<{
269
354
  /**
270
355
  * Output folder for generated files.
@@ -445,6 +530,28 @@ export type ExternalConfig = Partial<{
445
530
  * Prefer this over legacy `decorate`/`decorate_*` schema configuration.
446
531
  */
447
532
  decorators: DecoratorItem[];
533
+ /**
534
+ * Override `@Field()` decorator arguments for specific fields.
535
+ * Use this to customize pagination fields (take, skip, cursor) or other
536
+ * generated Args fields that don't come from your Prisma schema.
537
+ *
538
+ * Each rule is evaluated against generated field metadata (`FieldInfo`) and
539
+ * applied when `match` returns `true`.
540
+ *
541
+ * @example
542
+ * fieldDecoratorArguments: [
543
+ * {
544
+ * match: ({ objectName, propertyName }) =>
545
+ * objectName.endsWith('Args') && propertyName === 'take',
546
+ * decoratorArguments: {
547
+ * name: 'first',
548
+ * defaultValue: 10,
549
+ * description: 'Number of records to return',
550
+ * },
551
+ * },
552
+ * ]
553
+ */
554
+ fieldDecoratorArguments: FieldDecoratorRule[];
448
555
  }>;
449
556
  export declare function generate(args: GeneratorOptions & {
450
557
  skipAddOutputSourceFiles?: boolean;
@@ -633,6 +633,25 @@ class Configuration {
633
633
  }
634
634
  }
635
635
 
636
+ /**
637
+ * Get field override arguments for a specific field.
638
+ * Returns merged fieldArguments from all matching overrides.
639
+ */
640
+ getFieldOverride(args) {
641
+ const overrides = this.externalConfig?.fieldDecoratorArguments;
642
+ if (!overrides?.length) return;
643
+ let result;
644
+ for (const override of overrides) {
645
+ if (override.match(args)) {
646
+ result = {
647
+ ...result,
648
+ ...override.decoratorArguments
649
+ };
650
+ }
651
+ }
652
+ return result;
653
+ }
654
+
636
655
  /**
637
656
  * @deprecated Should be replaced by decorators
638
657
  */
@@ -644,6 +663,9 @@ class Configuration {
644
663
  }
645
664
  }
646
665
 
666
+ function isWhereUniqueInputType(name) {
667
+ return name.endsWith('WhereUniqueInput');
668
+ }
647
669
  function isManyAndReturnOutputType(name) {
648
670
  const lowerName = name.toLowerCase();
649
671
  if ((lowerName.startsWith('createmany') || lowerName.startsWith('updatemany')) && (lowerName.endsWith('andreturnoutputtype') || lowerName.endsWith('andreturn'))) {
@@ -1312,10 +1334,6 @@ function createFieldName(args) {
1312
1334
  return name || fields.join('_');
1313
1335
  }
1314
1336
 
1315
- function isWhereUniqueInputType(name) {
1316
- return name.endsWith('WhereUniqueInput');
1317
- }
1318
-
1319
1337
  /**
1320
1338
  * Get property structure (field) for class.
1321
1339
  */
@@ -1492,7 +1510,7 @@ function inputType(args) {
1492
1510
  graphqlType = graphqlImport.name;
1493
1511
  let referenceName = propertyType[0];
1494
1512
  if (location === 'enumTypes') {
1495
- referenceName = last(referenceName.split(' '));
1513
+ referenceName = last(referenceName.split(' ')) ?? 'any';
1496
1514
  }
1497
1515
  if (graphqlImport.specifier && !importDeclarations.has(graphqlImport.name) && graphqlImport.name !== inputType.name
1498
1516
  // ((graphqlImport.name !== inputType.name && !shouldHideField) ||
@@ -1514,11 +1532,15 @@ function inputType(args) {
1514
1532
  name: 'HideField'
1515
1533
  });
1516
1534
  } else {
1535
+ // Get field overrides from config
1536
+ const fieldOverride = config.getFieldOverride(fieldInfo);
1537
+
1517
1538
  // Generate `@Field()` decorator
1518
1539
  property.decorators.push({
1519
1540
  arguments: [isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`, JSON5.stringify({
1520
1541
  ...settings?.fieldArguments(),
1521
- nullable: !isRequired
1542
+ nullable: !isRequired,
1543
+ ...fieldOverride
1522
1544
  })],
1523
1545
  name: 'Field'
1524
1546
  });
@@ -2053,6 +2075,7 @@ function modelOutputType(outputType, args) {
2053
2075
  property.decorators.push(generateFieldDecorator({
2054
2076
  config,
2055
2077
  field,
2078
+ fieldInfo,
2056
2079
  graphqlType,
2057
2080
  modelField,
2058
2081
  settings
@@ -2121,6 +2144,7 @@ function generateFieldDecorator(args) {
2121
2144
  const {
2122
2145
  config,
2123
2146
  field,
2147
+ fieldInfo,
2124
2148
  graphqlType,
2125
2149
  modelField,
2126
2150
  settings
@@ -2143,11 +2167,15 @@ function generateFieldDecorator(args) {
2143
2167
  return Boolean(isNullable);
2144
2168
  };
2145
2169
  const defaultValue = ['number', 'string', 'boolean'].includes(typeof modelField?.default) ? modelField?.default : undefined;
2170
+
2171
+ // Get field overrides from config
2172
+ const fieldOverride = config.getFieldOverride(fieldInfo);
2146
2173
  const secondArgumentOptions = JSON5.stringify({
2147
2174
  ...settings?.fieldArguments(),
2148
2175
  defaultValue,
2149
2176
  description: modelField?.documentation,
2150
- nullable: getNullable()
2177
+ nullable: getNullable(),
2178
+ ...fieldOverride
2151
2179
  });
2152
2180
  return {
2153
2181
  arguments: [firstArgumentType, secondArgumentOptions],
@@ -2337,11 +2365,15 @@ function outputType(outputType, args) {
2337
2365
  name: 'HideField'
2338
2366
  });
2339
2367
  } else {
2368
+ // Get field overrides from config
2369
+ const fieldOverride = config.getFieldOverride(fieldInfo);
2370
+
2340
2371
  // Generate `@Field()` decorator
2341
2372
  property.decorators.push({
2342
2373
  arguments: [isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`, JSON5.stringify({
2343
2374
  ...settings?.fieldArguments(),
2344
- nullable: Boolean(field.isNullable)
2375
+ nullable: Boolean(field.isNullable),
2376
+ ...fieldOverride
2345
2377
  })],
2346
2378
  name: 'Field'
2347
2379
  });