UNPKG

prisma-nestjs-graphql

Version:

Generate object types, inputs, args, etc. from prisma schema file for usage with @nestjs/graphql module

1,019 lines (792 loc) 26.4 kB
# prisma-nestjs-graphql Generate object types, inputs, args, etc. from prisma schema file for usage with @nestjs/graphql module. ## Features - Generates only necessary imports - Combines zoo of nested/nullable filters - Does not generate resolvers, since it's application specific ## Install ``` npm install --save-dev prisma-nestjs-graphql @prisma/generator-helper identity-type ``` ## Usage 1. Add new generator section to `schema.prisma` file ```prisma generator nestgraphql { provider = "prisma-nestjs-graphql" // Or explicit node execution provider = "node node_modules/prisma-nestjs-graphql/bin.mjs" output = "../src/@generated" } ``` 2. Add a configuration file Create `prisma/nestgraphql.config.mjs` and reference it in your schema: ```prisma generator nestgraphql { provider = "prisma-nestjs-graphql" configFile = "./nestgraphql.config.mjs" } ``` ```js // prisma/nestgraphql.config.mjs /** * @type {import('prisma-nestjs-graphql').ExternalConfig} */ export default { output: '../src/@generated', }; ``` All generator options—including `decorators`, `fields`, `useInputType`, `customImport`, and `graphqlScalars`—are defined as **structured JavaScript objects** in the config file instead of using underscore-delimited keys (`decorate_1_*`, `fields_Validator_*`, etc.) directly in the schema. 3. Run prisma generate ```sh npx prisma generate ``` 4. If your models have `Decimal` and `Json` types, you need install: ```sh npm install graphql-type-json prisma-graphql-type-decimal ``` - [graphql-type-json](https://github.com/taion/graphql-type-json) - [prisma-graphql-type-decimal](https://github.com/unlight/prisma-graphql-type-decimal) Or write you own graphql scalar types, [read more on docs.nestjs.com](https://docs.nestjs.com/graphql/scalars). ## Configuration File Instead of defining all options as flat keys in `schema.prisma`, you can use a structured JavaScript config file for better readability, validation, and IDE autocompletion. ### Migration from flatten-style keys **Before** (underscore-delimited keys in `schema.prisma`): ``` fields_Validator_from = "class-validator" fields_Validator_input = true decorate_1_type = "CreateOneUserArgs" decorate_1_field = data decorate_1_from = "class-validator" decorate_1_name = ValidateNested decorate_1_arguments = "[]" ``` **After** (structured objects in `nestgraphql.config.mjs`): ```js fields: { Validator: { from: 'class-validator', input: true }, }, decorators: [ { match: ({ objectName, propertyName }) => objectName === 'CreateOneUserArgs' && propertyName === 'data', from: 'class-validator', name: 'ValidateNested', arguments: [], namedImport: true, }, ], ``` ### Backward compatibility The old flatten-style keys still work if no `configFile` is specified. Options from the config file take precedence. ## Generator options #### `output` Output folder, if path relative and defined in schema it will be relative to schema, if defined in config file it will be relative to this config file. Type: `string` #### `outputFilePattern` File path and name pattern Type: `string` Default: `{model}/{name}.{type}.ts` Possible tokens: - `{model}` Model name in dashed case or 'prisma' if unknown - `{name}` Dashed-case name of model/input/arg without suffix - `{type}` Short type name (model, input, args, output) - `{plural.type}` Plural short type name (models, inputs, enums) #### `tsConfigFilePath` Path to `tsconfig.json` (absolute path or relative to current working directory) Type: `string | undefined` Default: `tsconfig.json` if exists, `undefined` otherwise #### `prismaClientImport` The path to use to import the Prisma Client package Type: `string | undefined` Default: `@prisma/client` #### `importExtension` Append an extension to relative import and export module specifiers (without dot, eg. 'js, 'ts', 'mjs') Type: `string` Default: `''` (empty string) #### `combineScalarFilters` Combine nested/nullable scalar filters to single Type: `boolean` Default: `true` #### `noAtomicOperations` Remove input types for atomic operations Type: `boolean` Default: `true` #### `reExport` Create `index.ts` file with re-export Type: `enum` Values: `None` Default, create nothing `Directories` Create index file in all root directories `Single` Create single index file in output directory `All` Create index file in all root directories and in output directory Example configuration: ```prisma generator nestgraphql { provider = "node node_modules/prisma-nestjs-graphql" output = "../src/@generated" reExport = Directories } ``` #### `emitSingle` Generate single file with merged classes and enums. Type: `boolean` Default: `false` #### `emitCompiled` Emit compiled JavaScript and definitions instead of TypeScript sources. Type: `boolean` Default: `false` #### `emitBlocks` Emit only selected blocks. Be aware, that some blocks do depend on others, e.g. one can't emit `models` without emitting `enums`. Type: `("args" | "inputs" | "outputs" | "models" | "enums")[]` Default: `["args", "inputs", "outputs", "models", "enums"]` #### `omitModelsCount` Omit `_count` field from models. Type: `boolean` Default: `false` #### `purgeOutput` Delete all files in `output` folder. Type: `boolean` Default: `false` #### `noTypeId` Disable usage of graphql `ID` type and use `Int/Float` for fields marked as `@id` in schema. Type: `boolean` Default: `false` #### `typeListNullable` Adds `nullable: true` for relation list properties out output models, it makes graphql field looks like `[Type!]`, default `[Type!]!` Type: `boolean` Default: `false` #### `requireSingleFieldsInWhereUniqueInput` When a model `*WhereUniqueInput` class has only a single field, mark that field as **required** (TypeScript) and **not nullable** (GraphQL). See [#58](https://github.com/unlight/prisma-nestjs-graphql/issues/58) for more details. Type: `boolean` Default: `false` **Note**: It will break compatiblity between Prisma types and generated classes. #### `unsafeCompatibleWhereUniqueInput` Set TypeScript property type as non optional for all fields in `*WhereUniqueInput` classes. See [#177](https://github.com/unlight/prisma-nestjs-graphql/issues/177) for more details. Type: `boolean` Default: `false` #### `inputType` Since GraphQL does not support input union type, this setting map allow to choose which input type is preferable. **New (config file)**: ```js /** * Input type mapping. * Select which input type should be exposed when multiple candidates exist. * Since GraphQL does not support input unions, this setting can resolve * ambiguous fields (e.g. `UserRelationFilter` vs `UserWhereInput`). * * Supports two variants: * - object map: `{ [inputTypeName]: { [fieldName|'*']: pattern } }` * - function: return either an `InputTypeRef` or a string pattern * (same matching behavior as map patterns, including `match:` syntax) * @example * // Force all WhereInput relation properties to use the plain WhereInput type: * { WhereInput: { '*': 'WhereInput' } } * // Or for a specific property in a specific type: * { PostCreateInput: { author: 'UserCreateNestedOneWithoutPostsInput' } } * // Function variant returning a pattern: * ({ inputTypeName, fieldName }) => * inputTypeName.includes('CreateOne') && fieldName === 'data' * ? 'UncheckedCreate' * : undefined */ inputType: GetInputTypeFunction | ConfigInputTypeMap; ``` Where: - `typeName` — Full or partial name of the class where need to choose input type - `property` — Property of the class. Special case `ALL` means any/all properties - `pattern` — Part of name (or full) of type to choose; use `match:*UncheckedCreateInput` for wildcard/negation matching **Legacy (schema.prisma, flatten-style)**: ```sh generator nestgraphql { useInputType_{typeName}_{property} = "{pattern}" } ``` Example: ```ts export type PostWhereInput = { author?: XOR<UserRelationFilter, UserWhereInput>; }; export type UserRelationFilter = { is?: UserWhereInput; isNot?: UserWhereInput; }; export type UserWhereInput = { AND?: Enumerable<UserWhereInput>; OR?: Enumerable<UserWhereInput>; NOT?: Enumerable<UserWhereInput>; id?: StringFilter | string; name?: StringFilter | string; }; ``` We have generated types above, by default property `author` will be decorated as `UserRelationFilter`, to set `UserWhereInput` need to configure generator the following way (legacy way): ```prisma generator nestgraphql { provider = "node node_modules/prisma-nestjs-graphql" output = "../src/@generated" useInputType_WhereInput_ALL = "WhereInput" } ``` ```ts @InputType() export class PostWhereInput { @Field(() => UserWhereInput, { nullable: true }) author?: UserWhereInput; } ``` #### `decorators` Modern way to attach custom decorators in config file (`decorators: DecoratorItem[]`). ```js decorators: [ { match: ({ objectName, propertyName }) => objectName === 'CreateOneUserArgs' && propertyName === 'data', from: 'class-validator', name: 'ValidateNested', arguments: [], namedImport: true, }, { match: ({ objectName, propertyName }) => objectName === 'CreateOneUserArgs' && propertyName === 'data', from: 'class-transformer', name: 'Type', arguments: ['() => {propertyType.0}'], namedImport: true, }, ]; ``` `match` receives one argument `FieldInfo`: ```ts type FieldInfo = { /** * Prisma DMMF field location type * Can be: 'scalar', 'inputObjectTypes', 'outputObjectTypes', 'enumTypes', 'fieldRefTypes' */ location: FieldLocation; /** * Class name */ objectName: string; /** * Property name */ propertyName: string; /** * Property type (may contain TypeScript elements, like parameters for generics, etc.) */ propertyType: string; /** * GraphQL/Prisma type name */ typeName: string; }; ``` Decorator item type: ```ts type DecoratorItem = { /** Return `true` to apply this decorator to the current field. */ match: (args: FieldInfo) => boolean; /** Arguments passed to the decorator call. * Supports templates like `{propertyType.0}`. */ arguments?: string[]; /** Module specifier to import from (e.g. 'class-validator') */ from: string; /** Decorator name. Can include namespace, e.g. `Transform.Type`. */ name: string; /** Import as a named export. */ namedImport: boolean; /** Import as default export. * Use `true` to import by decorator name. */ defaultImport?: string | true; /** Import entire module under this namespace. */ namespaceImport?: string; }; ``` Special token in arguments: `{propertyType.0}` resolves to the field's TypeScript type. Example result: ```ts import { ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; @ArgsType() export class CreateOneUserArgs { @Field(() => UserCreateInput, { nullable: false }) @ValidateNested() @Type(() => UserCreateInput) data!: UserCreateInput; } ``` **Legacy (`decorate` / `decorate_*`)** Legacy flatten-style keys in `schema.prisma` are still supported for backward compatibility: ```sh generator nestgraphql { decorate_{key}_type = "outmatch pattern" decorate_{key}_field = "outmatch pattern" decorate_{key}_from = "module specifier" decorate_{key}_name = "import name" decorate_{key}_arguments = "[argument1, argument2]" decorate_{key}_defaultImport = "default import name" | true decorate_{key}_namespaceImport = "namespace import name" decorate_{key}_namedImport = "import name" | true } ``` Prefer `decorators` in `nestgraphql.config.*` for new configuration. #### `graphqlScalars` Allow to set custom graphql type for Prisma scalar type. **New (config file)**: ```js graphqlScalars: { BigInt: { name: 'GraphQLBigInt', specifier: 'graphql-scalars' }, } ``` **Legacy (schema.prisma, flatten-style)**: ``` graphqlScalars_{type}_name = "string" graphqlScalars_{type}_specifier = "string" ``` where `{type}` is a prisma scalar type name (e.g. BigInt) Example: ``` graphqlScalars_BigInt_name = "GraphQLBigInt" graphqlScalars_BigInt_specifier = "graphql-scalars" ``` May generate: ```ts import { GraphQLBigInt } from 'graphql-scalars'; export class BigIntFilter { @Field(() => GraphQLBigInt, { nullable: true }) equals?: bigint | number; } ``` It will affect all inputs and outputs types (including models). #### `customImports` Allow to declare custom import statements. **Note**: Only works with `emitSingle = true` **New (config file)**: ```js customImports: [ { from: 'nestjs-i18n', name: 'I18n', defaultImport: true }, { from: 'class-transformer', name: 'Transform', namedImport: true }, ]; ``` **Legacy (schema.prisma, flatten-style)**: ```sh generator nestgraphql { customImport_{key}_from = "module specifier" customImport_{key}_name = "import name" customImport_{key}_defaultImport = "default import name" | true customImport_{key}_namespaceImport = "namespace import name" customImport_{key}_namedImport = "import name" | true } ``` Where `{key}` any identifier to group values (written in [flatten](https://github.com/hughsk/flat) style) - `customImport_{key}_from` - module specifier to import from (e.g `nestjs-i18n`) - `customImport_{key}_name` - import name or name with namespace - `customImport_{key}_defaultImport` - import as default - `customImport_{key}_namespaceImport` - use this name as import namespace - `customImport_{key}_namedImport` - named import (without namespace) #### `fieldDecoratorArguments` 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. Each rule is evaluated against generated field metadata and applied when the `match` function returns `true`. Multiple matching rules are merged in order. **New (config file)**: ```js fieldDecoratorArguments: [ { match: ({ objectName, propertyName }) => objectName.endsWith('Args') && propertyName === 'take', decoratorArguments: { name: 'first', defaultValue: 10, description: 'Number of records to return', }, }, { match: ({ objectName, propertyName }) => objectName.endsWith('Args') && propertyName === 'skip', decoratorArguments: { name: 'offset', defaultValue: 0, description: 'Number of records to skip', }, }, ] ``` **Available `decoratorArguments` options:** - `nullable` — Mark field as nullable in GraphQL schema - `defaultValue` — Default value for the field - `description` — Description shown in GraphQL schema - `deprecationReason` — Mark field as deprecated - `name` — Custom name for the field in GraphQL schema (TypeScript property name stays the same) - `complexity` — Complexity value for query cost analysis - `middleware` — Middleware function name(s) to apply to the field **Middleware Example:** To use field middleware, combine `customImports` with `fieldDecoratorArguments`. Middleware values are emitted as identifier references (not string literals), allowing them to reference your imported middleware functions: ```js export default { customImports: [ { from: './middleware/logger', name: 'loggerMiddleware', defaultImport: true }, { from: './middleware/auth', name: 'authMiddleware', defaultImport: true }, ], fieldDecoratorArguments: [ { match: ({ objectName, propertyName }) => objectName === 'User' && propertyName === 'email', decoratorArguments: { middleware: ['loggerMiddleware', 'authMiddleware'], description: 'User email with logging and auth', }, }, ], }; ``` This generates: ```ts @Field(() => String, { description: 'User email with logging and auth', middleware: [loggerMiddleware, authMiddleware] }) email: string; ``` **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. The `match` function receives `FieldInfo` with: - `objectName` — Class name (e.g., 'FindManyUserArgs') - `propertyName` — Property name (e.g., 'take', 'skip') - `propertyType` — TypeScript property type - `typeName` — GraphQL/Prisma type name - `location` — Prisma field location ('scalar', 'inputObjectTypes', etc.) ## Documentation and field options Comments with triple slash will projected to typescript code comments and some `@Field()` decorator options For example: ```prisma model Product { /// Old description /// @deprecated Use new name instead /// @complexity 1 oldName String } ``` May produce: ```ts @ObjectType() export class Product { /** * Old description * @deprecated Use new name instead */ @Field(() => String, { description: 'Old description', deprecationReason: 'Use new name instead', complexity: 1, }) oldName: string; } ``` ## Field Settings Special directives in triple slash comments for more precise code generation. #### @HideField() Removes field from GraphQL schema. By default (without arguments) field will be decorated for hide only in output types (type in schema). To hide field in input types add `input: true`. To hide field in specific type you can use glob pattern `match: string | string[]` see [outmatch](https://github.com/axtgr/outmatch#usage) for details. For config-file based rules, use `shouldHideField`: ```js export default { shouldHideField: ({ location, objectName, propertyName }) => location === 'inputObjectTypes' && objectName.endsWith('CreateInput') && ['id', 'createdAt', 'updatedAt'].includes(propertyName), }; ``` The callback receives `FieldInfo` (`location`, `objectName`, `propertyName`, `propertyType`, `typeName`). When `shouldHideField` is defined, it overrides `@HideField(...)` settings from field comments and legacy `decorate` rules. Examples: - `@HideField()` same as `@HideField({ output: true })` - `@HideField({ input: true, output: true })` - `@HideField({ match: 'UserCreate*Input' })` ```prisma model User { id String @id @default(cuid()) /// @HideField() password String /// @HideField({ output: true, input: true }) secret String /// @HideField({ match: '@(User|Comment)Create*Input' }) createdAt DateTime @default(now()) } ``` May generate classes: ```ts @ObjectType() export class User { @HideField() password: string; @HideField() secret: string; @Field(() => Date, { nullable: false }) createdAt: Date; } ``` ```ts @InputType() export class UserCreateInput { @Field() password: string; @HideField() secret: string; @HideField() createdAt: Date; } ``` #### Custom Decorators Applying custom decorators requires configuration of generator. ```prisma // Legacy configuration, prefer config file generator nestgraphql { fields_{namespace}_from = "module specifier" fields_{namespace}_input = true | false fields_{namespace}_output = true | false fields_{namespace}_model = true | false fields_{namespace}_defaultImport = "default import name" | true fields_{namespace}_namespaceImport = "namespace import name" fields_{namespace}_namedImport = true | false } ``` Create configuration map in [flatten](https://github.com/hughsk/flat) style for `{namespace}`. Where `{namespace}` is a namespace used in field triple slash comment. ##### `fields_{namespace}_from` Required. Name of the module, which will be used in import (`class-validator`, `graphql-scalars`, etc.) Type: `string` ##### `fields_{namespace}_input` Means that it will be applied on input types (classes decorated by `InputType`) Type: `boolean` Default: `false` ##### `fields_{namespace}_output` Means that it will be applied on output types (classes decorated by `ObjectType`), including models Type: `boolean` Default: `false` ##### `fields_{namespace}_model` Means that it will be applied only on model types (classes decorated by `ObjectType`) Type: `boolean` Default: `false` ##### `fields_{namespace}_defaultImport` Default import name, if module have no namespace. Type: `undefined | string | true` Default: `undefined` If defined as `true` then import name will be same as `{namespace}` ##### `fields_{namespace}_namespaceImport` Import all as this namespace from module Type: `undefined | string` Default: Equals to `{namespace}` ##### `fields_{namespace}_namedImport` If imported module has internal namespace, this allow to generate named import, imported name will be equal to `{namespace}`, see [example of usage](#propertytype) Type: `boolean` Default: `false` Custom decorators example: ```prisma generator nestgraphql { fields_Validator_from = "class-validator" fields_Validator_input = true } model User { id Int @id /// @Validator.MinLength(3) name String } ``` May generate following class: ```ts import { InputType, Field } from '@nestjs/graphql'; import * as Validator from 'class-validator'; @InputType() export class UserCreateInput { @Field(() => String, { nullable: false }) @Validator.MinLength(3) name!: string; } ``` Custom decorators can be applied on classes (models): ``` /// @NG.Directive('@extends') /// @NG.Directive('@key(fields: "id")') model User { /// @NG.Directive('@external') id String @id } generator nestgraphql { fields_NG_from = "@nestjs/graphql" fields_NG_output = false fields_NG_model = true } ``` May generate: ```ts import * as NG from '@nestjs/graphql'; @NG.Directive('@extends') @NG.Directive('@key(fields: "id")') export class User { @Field(() => ID, { nullable: false }) @NG.Directive('@external') id!: string; ``` #### @FieldType() Allow set custom GraphQL scalar type for field To override scalar type in specific classes, you can use glob pattern `match: string | string[]` see [outmatch](https://github.com/axtgr/outmatch#usage) for details. ```prisma model User { id Int @id /// @FieldType({ name: 'Scalars.GraphQLEmailAddress', from: 'graphql-scalars', input: true }) email String } ``` May generate following class: ```ts import { InputType, Field } from '@nestjs/graphql'; import * as Scalars from 'graphql-scalars'; @InputType() export class UserCreateInput { @Field(() => Scalars.GraphQLEmailAddress, { nullable: false }) email!: string; } ``` And following GraphQL schema: ```grapqhl scalar EmailAddress input UserCreateInput { email: EmailAddress! } ``` Same field type may be used in different models and it is not convenient to specify every time all options. There is a shortcut: ```grapqhl generator nestgraphql { fields_Scalars_from = "graphql-scalars" fields_Scalars_input = true fields_Scalars_output = true } model User { id Int @id /// @FieldType('Scalars.GraphQLEmailAddress') email String } ``` The result will be the same. `Scalars` is the namespace here. Missing field options will merged from generator configuration. #### @PropertyType() Similar to `@FieldType()` but refer to TypeScript property (actually field too). To override TypeScript type in specific classes, you can use glob pattern `match: string | string[]` see [outmatch](https://github.com/axtgr/outmatch#usage) for details. Example: ``` generator nestgraphql { fields_TF_from = "type-fest" } model User { id String @id /// @PropertyType('TF.JsonObject') data Json } ``` May generate: ```ts import * as TF from 'type-fest'; @ObjectType() export class User { @Field(() => GraphQLJSON) data!: TF.JsonObject; } ``` ### @Directive() Allow attach `@Directive` decorator from `@nestjs/graphql` GraphQL federation example: ``` /// @Directive({ arguments: ['@extends'] }) /// @Directive({ arguments: ['@key(fields: "id")'] }) model User { /// @Directive({ arguments: ['@external'] }) id String @id } ``` May generate: ```ts @ObjectType() @Directive('@extends') @Directive('@key(fields: "id")') export class User { @Field(() => ID, { nullable: false }) @Directive('@external') id!: string; } ``` #### @ObjectType() Allow rename type in schema and mark as abstract. Example 1: ``` // schema.prisma /// @ObjectType({ isAbstract: true }) model User { id Int @id } ``` ```ts @ObjectType({ isAbstract: true }) export class User {} ``` Example 2: ``` // schema.prisma /// @ObjectType('Human', { isAbstract: true }) model User { id Int @id } ``` ```ts @ObjectType('Human', { isAbstract: true }) export class User {} ``` ### Using library in other generators ```ts import { generate } from 'prisma-nestjs-graphql'; ``` ## Similar Projects - <https://github.com/jasonraimondi/prisma-generator-nestjs-graphql> - <https://github.com/omar-dulaimi/prisma-class-validator-generator> - <https://github.com/kimjbstar/prisma-class-generator> - <https://github.com/odroe/nest-gql-mix> - <https://github.com/rfermann/nestjs-prisma-graphql-generator> - <https://github.com/madscience/graphql-codegen-nestjs> - <https://github.com/wSedlacek/prisma-generators/tree/master/libs/nestjs> - <https://github.com/EndyKaufman/typegraphql-prisma-nestjs> - <https://github.com/MichalLytek/typegraphql-prisma> - <https://github.com/mk668a/nestjs-prisma-graphql-crud-gen> ## Resources - Todo - <https://github.com/unlight/prisma-nestjs-graphql/issues/2> - <https://github.com/prisma/prisma/blob/main/packages/client/src/generation/TSClient/TSClient.ts> - <https://ts-ast-viewer.com/> - <https://github.com/unlight/nestjs-graphql-prisma-realworld-example-app> - <https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/data-model> - JSON type for the code first approach - <https://github.com/nestjs/graphql/issues/111#issuecomment-631452899> - <https://github.com/paljs/prisma-tools/tree/master/packages/plugins> - <https://github.com/wasp-lang/wasp> ## TODO - CommentUncheckedUpdateManyWithoutAuthorNestedInput and CommentUpdateManyWithoutAuthorNestedInput are same - Add logic to detect view models and skip generation of mutation inputs/args for them https://github.com/unlight/prisma-nestjs-graphql/issues/248 - dummy-createfriends.input.ts -> `create-friends` - check 'TODO FIXME' - rework test, use setup file ## License [MIT License](https://opensource.org/licenses/MIT) (c) 2026