環境構築

はじめに―TypeScript✕GraphQLの組み合わせ

前のページでも述べた通り、GraphQLはAPIのための強力なクエリ言語であり、既存のデータでクエリを実行するための実行環境でもあります。これは、REST APIで一般的に見られる多くの問題を解決するエレガントなアプローチです。背景については、GraphQLとRESTの比較を読んで下さい。
GraphQLとTypeScriptを組み合わせることで、GraphQLクエリの型安全性を向上させてエンドツーエンドの型付けを行うことができます。
本セクションでは、GraphQLの基本的な理解を前提として組み込みの@nestjs/graphqlモジュールの操作方法にフォーカスします。GraphQLモジュールは、Apolloサーバ(@nestjs/apolloドライバ)およびMercurius(@nestjs/mercuriusを使う)を使うように設定できます。
これらの実績あるGraphQLのパッケージの公式インテグレーションを提供することで、NestJSでGraphQLを簡単に利用できます。

インストール

まずは、以下のコマンドを入力してください。
# For Express and Apollo (default)
npm i @nestjs/graphql @nestjs/apollo graphql apollo-server-express
# For Fastify and Apollo
# npm i @nestjs/graphql @nestjs/apollo graphql apollo-server-fastify
# For Fastify and Mercurius
# npm i @nestjs/graphql @nestjs/mercurius graphql [email protected]^9
注意
@nestjs/[email protected]>=9@nestjs/apollo^10のパッケージはApollo v3に対応していますが (詳細はApollo Server 3 migration guideを参照してください)、 @nestjs/[email protected]^8はApollo v2([email protected]パッケージなど) のみ対応しています。

概観

NestJSでは、GraphQLアプリを開発する方法として、コードファースト方式とスキーマファースト方式の2つを提供しています。これはあなたにとって最適な方を選んでください。このGraphQLのセクションでは、コードファーストとスキーマファーストの2つに大きく分かれています。
コードファーストのアプローチでは、デコレータとTypeScriptクラスを使って対応するGraphQLスキーマを生成します。このアプローチはTypeScriptのみで作業を行い、言語構文間のコンテキスト切り替えを避けたい場合に有効です。
スキーマファーストのアプローチでは、GraphQL SDLファイルを扱います。SDL(Schema Definition Language)は言語に関係なく、異なるプラットフォーム間でスキーマを共有するための方法です。NestJSはGraphQLスキーマに基づいてTypeScriptの定義を自動的に生成し、冗長な定形コードを書く手間を省略します。

GraphQL✕TypeScriptの環境構築

メモ
以下の章では、@nestjs/apolloパッケージを統合していきます。
パッケージのインストールが完了すれば、GraphQLModuleをインポートし、forRoot()スタティックメソッドで設定できます。
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
}),
],
})
export class AppModule {}
forRoot()メソッドは引数としてオプション・オブジェクトを受け取ります。これらのオプションは、基盤となるドライバのインスタンスに渡されます。
例えば、PlayGroundを無効にしてデバッグモードをオフにしたい時は以下のようにします。
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
debug: false,
playground: false,
}),
],
})
export class AppModule {}
この場合、これらのオプションはApolloServerコンストラクタに転送されます。

GraphQL PlayGround

PlayGroundは、ブラウザのGraphQL IDEで、デフォルトではGraphQLサーバと同じURLで利用できます。PlayGroundにアクセスするには、基本的なGraphQLサーバが設定されて、実行されている必要があります。今すぐ動作を確認するためには、本ページで動作するサンプルをインストールして構築できます。
または、これらのコードのサンプルに従っている場合はResolverのセクションの手順を完了するとPlayGroundにアクセスできるようになります。
この状態で、アプリケーションがバックグラウンドで動いている状態でWebブラウザを開き、http:localhost:3000/graphqlに移動できます。すると、以下のようにGraphQL PlayGroundが表示されます。
引用:NestJS公式docs

複数のエンドポイント

また、@nestjs/graphqlモジュールの便利な機能として、複数のエンドポイントを同時に提供できます。これにより、どのモジュールをどのエンドポイントに含めるかを決めることができます。
デフォルトでは、GraphQLはアプリ全体を通してResolverを検索します。このスキャンをモジュールのサブセットのみに制限するにはincludeプロパティを使用します。
GraphQLModule.forRoot({
include: [CatsModule],
}),
注意
一つのアプリケーションで複数のGraphQLエンドポイントでapollo-server-fastifyパッケージを使用する場合は、GraphQLModuleの設定でdisableHealthCheck設定を有効にすることを確認してください。

コードファースト

コードファーストのアプローチでは、デコレータとTypeScriptのクラスを使って対応するGraphQLのスキーマを生成します。
コードファーストのアプローチを使用するには、まずoptionsオブジェクトにautoSchemaFileプロパティを追加することがあります。
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
}),
autoSchemaFileプロパティの値は、自動的に生成されるスキーマが作成されるパスです。また、スキーマは直接書き込む形でメモリ内に生成できます。これを有効にするには、autoSchemaFileプロパティをtrueに設定します。
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true,
}),
デフォルトでは、生成されたスキーマの型は含まれるモジュールで定義されている順番に並べます。スキーマを辞書順に並べるためには、sortSchemaプロパティをtrueに設定します。
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
sortSchema: true,
}),
公式のサンプルは以下のリンクからアクセスできます。

スキーマファースト

スキーマファーストのアプローチを取るには、まずoptionsオブジェクトにtypePathsプロパティを追加します。typePathsプロパティは、GraphQLModuleがあなたの書いたGraphQL SDLスキーマ定義ファイルをどこで探すのかを示します。これらのファイルはメモリ上で結合されます。
これによって、スキーマをいくつかのファイルに分割し、Resolverの近くに配置できます。
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
typePaths: ['./**/*.graphql'],
}),
また、通常、GraphQL SDLの型に対応するTypeScriptの定義(classあるいはinterface)が必要になります。対応するTypeScriptの定義を手作業で行うのは大変なので、@nestjs/graphqlパッケージからTypeScriptの定義を自動生成しましょう。この機能を有効にするには、GraphQLModuleを設定する際にdefinitionsオプションプロパティを追加します。
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
typePaths: ['./**/*.graphql'],
definitions: {
path: join(process.cwd(), 'src/graphql.ts'),
},
}),
definitionsオブジェクトのpathプロパティは、生成されたTypeScriptの出力を保存する場所を示しています。デフォルトでは、生成されるすべてのTypeScriptの型はinterfaceとして作成されます。代わりにクラスを生成するには、outputAsプロパティに'class'という値を設定してください。
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
typePaths: ['./**/*.graphql'],
definitions: {
path: join(process.cwd(), 'src/graphql.ts'),
outputAs: 'class',
},
}),
上記の方法では、アプリケーションが起動するたびに動的にTypeScriptの定義が生成されます。あるいは、オンデマンドでこれらを生成するシンプルなスクリプトを構築するのが望ましいかもしれません。
例えば、以下のようなスクリプトをgenerate-typings.tsで作成します。
import { GraphQLDefinitionsFactory } from '@nestjs/graphql';
import { join } from 'path';
const definitionsFactory = new GraphQLDefinitionsFactory();
definitionsFactory.generate({
typePaths: ['./src/**/*.graphql'],
path: join(process.cwd(), 'src/graphql.ts'),
outputAs: 'class',
});
そして、以下のコマンドを入力してください。
ts-node generate-typing
メモ
予めスクリプトをコンパイルしておき(例:tscコマンドで実行)、nodeで実行することもできます。(とは言っても、ts-nodeを使えばTypeScriptをスクリプトファイルとして実行できるので前者の方を推奨します)
スクリプトのウォッチモード(拡張子.gqlファイルが変更されると自動的にコードを生成するモード)を有効にするには、generate()メソッドにwatchオプションを追加してください。
definitionsFactory.generate({
typePaths: ['./src/**/*.graphql'],
path: join(process.cwd(), 'src/graphql.ts'),
outputAs: 'class',
watch: true,
emitTypenameField: true,
skipResovlerArgs: true,
});
オブジェクトの種類ごとに追加の__typenameフィールドを自動生成するなら、emitTypenameFieldオプションを有効にします。あと、Resolverを引数のないプレーンフィールドとして生成するには、skipResolverArgsオプションを有効にします。