NestJSは、TypeScriptで記述するバックエンドアプリケーションフレームワークです。デフォルトで DI(Dependency Injection) の仕組みをサポートしており、テスト可能な構成を簡単に作ることができる特徴があります。
今回の記事ではNestJSを使用して最もシンプルなGraphQLサーバを構築します。
↓完成イメージ
GraphQLの基本
GraphQLは、RESTエンドポイントのように煩雑に管理されたエンドポイントではなく、1つのエンドポイントに対して厳密に型指定されたスキーマとしてAPIを実行します。
GraphQLについて深くは解説しませんが、以下のリンクがとても参考になります。初学者は一読しておくことをオススメします。
NestJSでGraphQL
NestJSを使用したGraphQLの開発には2つの方法があります。
- スキーマファースト
- コードファースト
スキーマファーストのアプローチでは GraphQL SDL(スキーマ定義言語)をもとにしてTypeScript定義を自動的に生成します。
一方でコードファーストのアプローチでは、デコレータとTypeScriptのクラスのみを使用して対応する GraphQL スキーマを生成します。
今回はコードファーストのアプローチでGraphQLサーバを作成していきます。
まず始めに nestjsのコマンドラインツール@nestjs/cli
をインストールしましょう。インストールができたら nest コマンドが使用できます。早速 NestJSアプリケーションを作成します。
$ npm i -g @nestjs/cli
$ nest new nest-graphql
作成されたNestJSアプリケーションを起動しましょう。
$ cd nest-graphql/
$ npm run start
> nest-graphql@0.0.1 start /Users/daisuke/work/nest-graphql
> ts-node -r tsconfig-paths/register src/main.ts
[Nest] 5868 - 2019-12-03 21:36:33 [NestFactory] Starting Nest application...
[Nest] 5868 - 2019-12-03 21:36:33 [InstanceLoader] AppModule dependencies initialized +28ms
[Nest] 5868 - 2019-12-03 21:36:33 [RoutesResolver] AppController {/}: +10ms
[Nest] 5868 - 2019-12-03 21:36:33 [RouterExplorer] Mapped {/, GET} route +16ms
[Nest] 5868 - 2019-12-03 21:36:33 [NestApplication] Nest application successfully started +6ms
ブラウザで localhost:3000 にアクセスして Hello Wold! が表示されれば準備OKです。
この状態ではまだRESTAPIの形式になっていますね。
GraphQL 関連ライブラリのインストール
GraphQLサーバを実装していきますので、まずは必要なライブラリをインストールします。
$ npm i --save @nestjs/graphql \
apollo-server-express \
graphql-tools
REST API用に作られていた app.module.ts を書き換えましょう。
Controller, Service の箇所を GraphQLModule として書き換えました。
.forRoot()
メソッドで playground: true
を宣言することで ブラウザ(http://localhost:3000/graphql)で GraphQL IDEを表示できます。autoSchemaFile
は自動的に生成されたスキーマが作成されるパスを示しています
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
@Module({
imports: [
GraphQLModule.forRoot({
playground: true,
autoSchemaFile: 'schema.graphql'
}),
],
})
export class AppModule {}
playground はアプリケーションがバックグラウンドで実行されている間に、Webブラウザーを開いて http://localhost:3000/graphql にアクセスすると表示できます。npm run start
を実行してアプリケーションを起動してからブラウザを開いてみましょう。
$ npm run start
> nest-graphql@0.0.1 start /Users/daisuke/work/nest-graphql
> ts-node -r tsconfig-paths/register src/main.ts
[Nest] 8832 - 2019-12-03 23:44:15 [NestFactory] Starting Nest application...
[Nest] 8832 - 2019-12-03 23:44:15 [InstanceLoader] AppModule dependencies initialized +26ms
[Nest] 8832 - 2019-12-03 23:44:15 [InstanceLoader] RecipesModule dependencies initialized +1ms
[Nest] 8832 - 2019-12-03 23:44:15 [InstanceLoader] GraphQLModule dependencies initialized +0ms
[Nest] 8832 - 2019-12-03 23:44:15 [NestApplication] Nest application successfully started +82ms
Moduleを作成
NestJSの流儀に従って、まずはModuleを作成します。例としてレシピの一覧が表示できるアプリケーションを想定しています。
$ nest generate module recipes
CREATE /src/recipes/recipes.module.ts (84 bytes)
UPDATE /src/app.module.ts (325 bytes)
app.module.ts に自動的に RecipesModule が追加されるので確認しておきましょう。
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { RecipesModule } from './recipes/recipes.module';
@Module({
imports: [
GraphQLModule.forRoot({
playground: true,
autoSchemaFile: 'schema.graphql',
}),
RecipesModule, // <-- 自動的に追加される
],
})
export class AppModule {}
Modelを作成
次に Model を作成します。
@nestjs/graphql のライブラリから各種デコレータで宣言するものを import します。
$ nest generate class recipes/recipe
CREATE /src/recipes/recipe.spec.ts (147 bytes)
CREATE /src/recipes/recipe.ts (23 bytes)
import { Field, ID, ObjectType } from '@nestjs/graphql';
@ObjectType()
export class Recipe {
@Field(type => ID)
id: string;
@Field()
title: string;
}
Resolverを作成
最後にクエリの操作を行うリゾルバを作成します。
$ nest generate resolver recipes
CREATE /src/recipes/recipes.resolver.spec.ts (477 bytes)
CREATE /src/recipes/recipes.resolver.ts (98 bytes)
UPDATE /src/recipes/recipes.module.ts (170 bytes)
このResolverに Query、Mutation、Subscriptionを実装していきます。
今回は簡単のため、データベースには接続せずにレシピの一覧を返却する処理(Query)を実装しています。
import { Resolver, Query, Args } from '@nestjs/graphql';
import { Recipe } from './recipe';
const recipeTable = [
{
id: '1',
title: '鯖の味噌煮',
},
{
id: '2',
title: 'ミートソーススパゲティ',
},
{
id: '3',
title: '豚の生姜焼',
},
];
@Resolver('Recipes')
export class RecipesResolver {
@Query(returns => [Recipe])
async recipes(): Promise<Recipe[]> {
return recipeTable;
}
}
ここまででディレクトリ構成は以下のようになっています。
$ tree -L 2
.
├── app.module.ts
├── main.ts
└── recipes
├── recipe.spec.ts
├── recipe.ts
├── recipes.module.ts
├── recipes.resolver.spec.ts
└── recipes.resolver.ts
スキーマの作成
あとはアプリケーションを起動するとスキーマが自動的に作成されます。
$ npm run start
scema.graphql にスキーマが自動的に作成されています。
# -----------------------------------------------
# !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
# !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
# -----------------------------------------------
type Query {
recipes: [Recipe!]!
}
type Recipe {
id: ID!
title: String!
}
動作確認をしましょう。
http://localhost:3000/graphql にアクセスしてクエリを実行します。
確かにフィールドごとに選択されて Query が実行できていますね。
NestJSを使用することで、モデルに対してデコレータを付与するだけでシンプルかつ簡単に実装できました。
NestJSは適切にDIをすることでコードのテスタビリティをあげることができる強力なフレームワークです。
GraphQLサーバを組む場合にも威力を発揮できる可能性があり魅力的ですね。