:-) 🏕

Code-First GraphQL server

TL;DR

Code-First GraphQL server

隅田川.js というイベントで発表してきました

初めてGraphQLサーバーを書くためのWAFとして自分は @nestjs を採用しました。理由は下記のようなものがあります。

GraphQL serverを書く順序

@nestjs/graphql を用いた開発の場合、Code-Firstな開発とSDL(Schema Definition Language)-Firstな開発手法を選択することができます。

Code-Firstを選択した場合、モデルとなるファイルに対してGraphQLのschemaとして公開するかをデコレーターを用いて明示していきます。

import { Field, ID, ObjectType } from "@nestjs/graphql";
import {
  Column,
  Entity,
  EntityRepository,
  PrimaryGeneratedColumn,
} from "typeorm";
import { BaseRepository } from "typeorm-transactional-cls-hooked";

@Entity("users")
@ObjectType()
export class User {
  @Field(() => ID)
  @PrimaryGeneratedColumn("uuid")
  readonly id: string;

  @Field()
  @Column()
  readonly name: string;
}

@EntityRepository(User)
export class UserRepository extends BaseRepository<User> {}

上記のサンプルでは同時に typeorm を用いてデータベースschemaの定義を行っています。 データベースに保存されている情報と、GraphQLを用いて外部に公開するschemaの一部をモデルファイルの中に集約して管理することができます。 以降、必要なResolverを定義すれば、GraphQLとして公開されるQueryやMutationをコードによって表現できます。

対してSDL-Firstなアプローチでサーバーを開発した際の流れとして

  1. GraphQLファイルを定義する
  2. 手動で作成した graphql ファイルよりTypeScript Interfaceとなる型情報生成する
  3. implements 等を用い型安全性を確保する
  4. graphql で生成したファイルに沿ったResolverを作成する

といった流れになるかと思います。この2つを比較した際にCode-Firstな開発のメリットは Resolverの実装とGraphQL定義との乖離をTypeScriptレベルで確認できる ことにあると考えています。また、nest.jsに関してはSDL-First、Code-Firstそれぞれに対してモデルファイルの作成(または生成)は必要であるため、記述量に関してもCode-Firstで作成される際にはメリットがあります。

Code-Firstによる記述のデメリットとして、デコレータが多段で定義されがち ということがあります。

Prisma | Hasura

GraphQLサーバーを構築するにあたり、これらの2つは十分検討に値する仕組みが出来上がっていると思います。

Prisma is a GraphQL ORM for your GraphQL (or REST) servers and not your frontend apps, kind of like a replacement for JDBC, SQL Alchemy, ActiveRecord and so on.

hasura の記事を引用 PrismaはGraphQL ORMだ、という表現をされています。

generator client {
  provider        = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

model User {
  email String  @unique
  id    Int     @id @default(autoincrement())
  name  String?
  posts Post[]
}

Prismaでは独自のSDLとして .prisma ファイルを作成します。 比較のためにnest.jsとPrismaを併用しているサンプルを引用します。

// https://github.com/prisma/prisma-examples/blob/latest/typescript/graphql-nestjs/src/user.ts
import "reflect-metadata";
import { Field, ID, ObjectType } from "@nestjs/graphql";
import { IsEmail } from "class-validator";
import { Post } from "./post";

@ObjectType()
export class User {
  @Field((type) => ID)
  id: number;

  @Field()
  @IsEmail()
  email: string;

  @Field((type) => String, { nullable: true })
  name?: string | null;

  @Field((type) => [Post], { nullable: true })
  posts?: [Post] | null;
}

@nestjs/graphql + typeorm の時と異なり、entityとなるfieldの定義と、GraphQLとして公開するSchemaの定義がprismaファイルとTypeScriptファイルに分離します。 記述量という点では typeorm を使わない @nestjs/graphql と言った感じなると思っています。database migrationやschemaの型やrelationといった情報を Prisma に移管できるようです。

上記まででは typeormprisma を好みで選択するようになる印象ですが、 Prismaはtypeormと異なりGraphQLに最適化されたdataloaderのような機構のクエリーを発行できるかもしれないので、 @nestjs/graphql + typeorm + dataloader といった記述量が削減できるかもしれないことや、Paginateに関するサポートがないか期待しています。

Hasura