🎉

NestJS/GraphQLでObjectやInputの定義を自動化したいあなたへ

2022/05/26に公開
3

はじめに

NestJS/GraphQLで開発を進めていくうちに、ObjectやInputを毎回定義するのが大変で自動化したいなーと思っているときに、ちょうどいいライブラリを見つけたのでその紹介です。

実装(prisma-nestjs-graphql)

インストール

npm install --save-dev prisma-nestjs-graphql

schema.prismaに追記とコード生成

schema.prismaに以下のコードを追記します。

schema.prisma
generator nestgraphql {
    provider = "node node_modules/prisma-nestjs-graphql"
    output = "../src/@generated/prisma-nestjs-graphql"
}

生成コマンドを叩けば完了です!

npx prisma generate

なかみ

フォルダ構成

現在schema.prismaにTeacher/Class/Studentを定義しているのですが、それぞれのモデルに対して一つずつフォルダが作成されました。

ファイル構成

Teacherフォルダの中身はこんな感じです。
生成されるファイルが多すぎてかなり文字が小さくなっちゃいました。
よく使うところで行くと、Teacher Object、Get arg、Create input、Update input、Delte inputが完備されています。

他にもCount ArgやUpsert inputなどもありとても便利そうです。

ファイル1例 (TeacherWhereInput)

また、TeacherWhereInputを除いてみるとAND/OR/NOT検索までついてくる優れものです!
Date型はlte/rteでの検索も可能です。

teacher-where.input.ts
import { Field } from '@nestjs/graphql';
import { InputType } from '@nestjs/graphql';
import { IntFilter } from '../prisma/int-filter.input';
import { StringFilter } from '../prisma/string-filter.input';
import { DateTimeFilter } from '../prisma/date-time-filter.input';
import { ClassRelationFilter } from '../class/class-relation-filter.input';

@InputType()
export class TeacherWhereInput {

    @Field(() => [TeacherWhereInput], {nullable:true})
    AND?: Array<TeacherWhereInput>;

    @Field(() => [TeacherWhereInput], {nullable:true})
    OR?: Array<TeacherWhereInput>;

    @Field(() => [TeacherWhereInput], {nullable:true})
    NOT?: Array<TeacherWhereInput>;

    @Field(() => IntFilter, {nullable:true})
    id?: IntFilter;

    @Field(() => IntFilter, {nullable:true})
    classId?: IntFilter;

    @Field(() => StringFilter, {nullable:true})
    name?: StringFilter;

    @Field(() => DateTimeFilter, {nullable:true})
    createdAt?: DateTimeFilter;

    @Field(() => DateTimeFilter, {nullable:true})
    updatedAt?: DateTimeFilter;

    @Field(() => ClassRelationFilter, {nullable:true})
    class?: ClassRelationFilter;
}

Extend

パスワードなど外に見せたくないカラムはどうするの?

schema.prisma@HideField()デコレーションをつければ大丈夫です。
スラッシュ3つでprisma-nestjs-graphqlのコマンドとして認識されます。

schema.prisma
/// @HideField()
password         String

inputにvalidaterを付与したい

schema.prismaにバリデーション情報を付与します。

fields_Validator_from= "class-validator"は、Validatorはclass-validatorからインポートされたメソッドだよ。という意味です。

decorateの部分は、数字でグルーピングされていて1の例でいくと、
CreateOne*Argsクラスのdataフィールドにclass-validatorのメソッドであるValidateNestedデコレーションを追加するよという意味になります。

ValidateNestedはネストされているinputに対してもバリデーションを有効にしてくれるデコレーションです。)

schema.prisma
generator nestgraphql {
    provider = "node node_modules/prisma-nestjs-graphql"
    output = "../src/@generated/prisma-nestjs-graphql"
    fields_Validator_from = "class-validator"
    fields_Validator_input = true
    decorate_1_type        = "CreateOne*Args"
    decorate_1_field       = data
    decorate_1_name        = ValidateNested
    decorate_1_from        = "class-validator"
    decorate_1_arguments   = "[]"
    decorate_2_type        = "CreateMany*Args"
    decorate_2_field       = data
    decorate_2_name        = ValidateNested
    decorate_2_from        = "class-validator"
    decorate_2_arguments   = "[]"
    decorate_3_type        = "UpdateOne*Args"
    decorate_3_field       = data
    decorate_3_name        = ValidateNested
    decorate_3_from        = "class-validator"
    decorate_3_arguments   = "[]"
    decorate_4_type        = "UpdateMany*Args"
    decorate_4_field       = data
    decorate_4_name        = ValidateNested
    decorate_4_from        = "class-validator"
    decorate_4_arguments   = "[]"
}

このルール付けの後に、schema.prismaに定義されているモデルにMaxLengthなどのデコレーションを追加していけば完了です!

schema.prisma
model Teacher {
  id        Int      @id @default(autoincrement())
  classId   Int      @unique
  /// @Validator.IsNotEmpty()
  /// @Validator.MaxLength(255)
  name      String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  class Class @relation("teacherToClass", fields: [classId], references: [id])

  @@map("teachers")
}

抜粋ですがこのように生成されます。

teacher-create.input.ts
@InputType()
export class TeacherCreateInput {

    @Field(() => String, {nullable:false})
    @Validator.IsNotEmpty()
    @Validator.MaxLength(255)
    name!: string;

    @Field(() => Date, {nullable:true})
    createdAt?: Date | string;

    @Field(() => Date, {nullable:true})
    updatedAt?: Date | string;

    @Field(() => ClassCreateNestedOneWithoutTeacherInput, {nullable:false})
    class!: ClassCreateNestedOneWithoutTeacherInput;
}
create-one-teacher.args.ts
@ArgsType()
export class CreateOneTeacherArgs {

    @Field(() => TeacherCreateInput, {nullable:false})
    @ValidateNested()
    data!: TeacherCreateInput;
}

まとめ

実装はほとんどないのに、様々なユースケースに対応できるファイルがたくさん自動生成とても便利そうでした。
ただ、これいつ使うんだ...みたいな機能もあったのでtoo mach感は少しあります...

公式サイト見た感じ他にもいろんな機能ありそうなので使い倒していきたいです。

参考サイト

公式github
https://github.com/unlight/prisma-nestjs-graphql#decorate

Discussion

fufu

参考になる記事ありがとうございます!

お尋ねしたいのですがnull許容項目に対してValidationを実行することは可能でしょうか?
例えば以下のようにValidatorを設定してageをnullで渡した場合、@Validator.IsInt()と@Validator.Min(0)に引っかかりエラーとなってしまいました。
nullの場合はバリデーションを行わず、値が入っていた場合のみバリデーションを実行するような書き方をご存知でしたら教えて頂きたいです。

@Field(() => Int, { nullable: true })
@Validator.IsInt()
@Validator.Min(0)
age?: number;
ゆゆゆゆゆゆ

コメントありがとうございます!
その部分はprisma-nestjs-graphqlというよりも、class-validaterの仕事なのでそちらで調べたほうがいいかもしれません...

僕が試してみた感じ、↓のデコレーションをつけたらうまくいきました!

/// @Validator.IsOptional()
fufu

なるほど、class-validaterで制御するものでしたか。
教えて頂きありがとうございます!