prisma-nestjs-graphql
v23.2.1
Published
Generate object types, inputs, args, etc. from prisma schema file for usage with @nestjs/graphql module
Downloads
90,817
Maintainers
Readme
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-typeUsage
- Add new generator section to
schema.prismafile
generator nestgraphql {
provider = "prisma-nestjs-graphql"
// Or explicit node execution
provider = "node node_modules/prisma-nestjs-graphql/bin.mjs"
output = "../src/@generated"
}- Add a configuration file
Create prisma/nestgraphql.config.mjs and reference it in your schema:
generator nestgraphql {
provider = "prisma-nestjs-graphql"
configFile = "./nestgraphql.config.mjs"
}// 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.
- Run prisma generate
npx prisma generate- If your models have
DecimalandJsontypes, you need install:
npm install graphql-type-json prisma-graphql-type-decimal
Or write you own graphql scalar types, read more on docs.nestjs.com.
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):
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:
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 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 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):
/**
* 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 typeproperty— Property of the class. Special caseALLmeans any/all propertiespattern— Part of name (or full) of type to choose; usematch:*UncheckedCreateInputfor wildcard/negation matching
Legacy (schema.prisma, flatten-style):
generator nestgraphql {
useInputType_{typeName}_{property} = "{pattern}"
}Example:
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):
generator nestgraphql {
provider = "node node_modules/prisma-nestjs-graphql"
output = "../src/@generated"
useInputType_WhereInput_ALL = "WhereInput"
}@InputType()
export class PostWhereInput {
@Field(() => UserWhereInput, { nullable: true })
author?: UserWhereInput;
}decorators
Modern way to attach custom decorators in config file (decorators: DecoratorItem[]).
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:
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:
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:
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:
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):
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:
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):
customImports: [
{ from: 'nestjs-i18n', name: 'I18n', defaultImport: true },
{ from: 'class-transformer', name: 'Transform', namedImport: true },
];Legacy (schema.prisma, flatten-style):
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 style)
customImport_{key}_from- module specifier to import from (e.gnestjs-i18n)customImport_{key}_name- import name or name with namespacecustomImport_{key}_defaultImport- import as defaultcustomImport_{key}_namespaceImport- use this name as import namespacecustomImport_{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):
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 schemadefaultValue— Default value for the fielddescription— Description shown in GraphQL schemadeprecationReason— Mark field as deprecatedname— Custom name for the field in GraphQL schema (TypeScript property name stays the same)complexity— Complexity value for query cost analysismiddleware— 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:
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:
@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 typetypeName— GraphQL/Prisma type namelocation— 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:
model Product {
/// Old description
/// @deprecated Use new name instead
/// @complexity 1
oldName String
}May produce:
@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 for details.
For config-file based rules, use shouldHideField:
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' })
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:
@ObjectType()
export class User {
@HideField()
password: string;
@HideField()
secret: string;
@Field(() => Date, { nullable: false })
createdAt: Date;
}@InputType()
export class UserCreateInput {
@Field()
password: string;
@HideField()
secret: string;
@HideField()
createdAt: Date;
}Custom Decorators
Applying custom decorators requires configuration of generator.
// 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 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
Type: boolean
Default: false
Custom decorators example:
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:
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:
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 for details.
model User {
id Int @id
/// @FieldType({ name: 'Scalars.GraphQLEmailAddress', from: 'graphql-scalars', input: true })
email String
}May generate following class:
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:
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:
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 for details.
Example:
generator nestgraphql {
fields_TF_from = "type-fest"
}
model User {
id String @id
/// @PropertyType('TF.JsonObject')
data Json
}May generate:
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:
@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
}@ObjectType({ isAbstract: true })
export class User {}Example 2:
// schema.prisma
/// @ObjectType('Human', { isAbstract: true })
model User {
id Int @id
}@ObjectType('Human', { isAbstract: true })
export class User {}Using library in other generators
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 (c) 2026
