# 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
```

## Usage

1. Add new generator section to `schema.prisma` file

```prisma
generator nestgraphql {
    provider = "node node_modules/prisma-nestjs-graphql"
    // for yarn monorepos
    // provider = "prisma-nestjs-graphql"
    output = "../src/@generated"
}
```

2. Run prisma generate

```sh
npx prisma generate
```

3. 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).

## Generator options

#### `output`

Output folder relative to this schema 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

#### `combineScalarFilters`

Combine nested/nullable scalar filters to single  
Type: `boolean`  
Default: `false`

#### `noAtomicOperations`

Remove input types for atomic operations  
Type: `boolean`  
Default: `false`

#### `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,
files will be compiled with `emitDecoratorMetadata:false`, because there is a problem
with temporal dead zone when generating merged file.  
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`

#### `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.

#### `useInputType`

Since GraphQL does not support input union type, this setting map
allow to choose which input type is preferable.

```sh
generator nestgraphql {
    useInputType_{typeName}_{property} = "{pattern}"
}
```

Where:

- `typeName` Full name or partial name of the class where need to choose input type.  
  Example: `UserCreateInput` full name, `WhereInput` partial name, matches `UserWhereInput`, `PostWhereInput`, etc.
- `property` Property of the class for which need to choose type. Special case name `ALL` means any / all properties.
- `pattern` Part of name (or full) of type which should be chosen, you can use
  wild card or negate symbols, in this case pattern should starts with `match:`,
  e.g. `match:*UncheckedCreateInput` see [outmatch](https://github.com/axtgr/outmatch#usage) for details.

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:

```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;
}
```

#### `decorate`

Allow to attach multiple decorators to any field of any type.

```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
}
```

Where `{key}` any identifier to group values (written in [flatten](https://github.com/hughsk/flat) style)

- `decorate_{key}_type` - outmatch pattern to match class name
- `decorate_{key}_field` - outmatch pattern to match field name
- `decorate_{key}_from` - module specifier to import from (e.g `class-validator`)
- `decorate_{key}_name` - import name or name with namespace
- `decorate_{key}_defaultImport` - import as default
- `decorate_{key}_namespaceImport` - use this name as import namespace
- `decorate_{key}_namedImport` - named import (without namespace)
- `decorate_{key}_arguments` - arguments for decorator (if decorator need to be called as function)  
  Special tokens can be used:
  - `{propertyType.0}` - field's type (TypeScript type annotation)

Example of generated class:

```ts
@ArgsType()
export class CreateOneUserArgs {
  @Field(() => UserCreateInput, { nullable: false })
  data!: UserCreateInput;
}
```

To make it validateable (assuming `UserCreateInput` already contains validation decorators from `class-validator`),
it is necessary to add `@ValidateNested()` and `@Type()` from `class-transformer`.

```sh
decorate_1_type = "CreateOneUserArgs"
decorate_1_field = data
decorate_1_name = ValidateNested
decorate_1_from = "class-validator"
decorate_1_arguments = "[]"
decorate_2_type = "CreateOneUserArgs"
decorate_2_field = data
decorate_2_from = "class-transformer"
decorate_2_arguments = "['() => {propertyType.0}']"
decorate_2_name = Type
```

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;
}
```

Another example:

```sh
decorate_2_namespaceImport = "Transform"
decorate_2_name = "Transform.Type"
```

```ts
import * as Transform from 'class-transformer';

@Transform.Type(() => UserCreateInput)
data!: UserCreateInput;

```

Add `@HideField()` decorator to nested types:

```
decorate_3_type = "*CreateNestedOneWithoutUserInput"
decorate_3_field = "!(create)"
decorate_3_name = "HideField"
decorate_3_from = "@nestjs/graphql"
decorate_3_arguments = "[]"
```

May generate following class:

```ts
@Field(() => ProfileCreateWithoutUserInput, { nullable: true })
create?: ProfileCreateWithoutUserInput;

@HideField()
connectOrCreate?: ProfileCreateOrConnectWithoutUserInput;

@HideField()
connect?: ProfileWhereUniqueInput;
```

#### `graphqlScalars`

Allow to set custom graphql type for Prisma scalar type.
Format:

```
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).

## 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
  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',
  })
  oldName: string;
}
```

## Field Settings

Special directives in triple slash comments for more precise code generation.

#### @HideField()

Removes field from GraphQL schema.  
Alias: `@TypeGraphQL.omit(output: true)`

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.

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.

```sh
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 {}
```

## 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
