NestJSで始めるGraphQLサーバ開発（コードファースト編）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/299e2b62-2ee2-656b-aba1-267d4f50b148.png)
NestJSは、TypeScriptで記述するバックエンドアプリケーションフレームワークです。デフォルトで DI(Dependency Injection) の仕組みをサポートしており、テスト可能な構成を簡単に作ることができる特徴があります。
今回の記事ではNestJSを使用して最もシンプルなGraphQLサーバを構築します。
↓完成イメージ
![nestjs-graphql.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/b5df3895-ef8d-bc4b-6cfa-c2fa8d9f72d4.gif)

# GraphQLの基本

GraphQLは、RESTエンドポイントのように煩雑に管理されたエンドポイントではなく、１つのエンドポイントに対して厳密に型指定されたスキーマとしてAPIを実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/ec70087f-6fca-e868-5c8f-893a4934d383.png)

GraphQLについて深くは解説しませんが、以下のリンクがとても参考になります。初学者は一読しておくことをオススメします。

- [公式](https://graphql.org/learn/)
- [チュートリアル](https://www.howtographql.com/)
- [youtubeのチュートリアル](https://www.youtube.com/playlist?list=PL4cUxeGkcC9iK6Qhn-QLcXCXPQUov1U7f)

# NestJSでGraphQL

NestJSを使用したGraphQLの開発には２つの方法があります。

- スキーマファースト
- コードファースト

スキーマファーストのアプローチでは GraphQL SDL（スキーマ定義言語）をもとにしてTypeScript定義を自動的に生成します。
一方でコードファーストのアプローチでは、デコレータとTypeScriptのクラスのみを使用して対応する GraphQL スキーマを生成します。

今回はコードファーストのアプローチでGraphQLサーバを作成していきます。
まず始めに nestjsのコマンドラインツール`@nestjs/cli` をインストールしましょう。インストールができたら nest コマンドが使用できます。早速 NestJSアプリケーションを作成します。

```
$ npm i -g @nestjs/cli
$ nest new nest-graphql
```
作成されたNestJSアプリケーションを起動しましょう。

```bash
$ 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の形式になっていますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/8519cd40-6bc6-171f-9860-4ff263521974.png)


# GraphQL 関連ライブラリのインストール

GraphQLサーバを実装していきますので、まずは必要なライブラリをインストールします。

```bash
$ 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` は自動的に生成されたスキーマが作成されるパスを示しています

```javascript:app.module.ts
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
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/79cb1a4f-de85-67b7-2989-47b0c8b3cf1c.png)



# Moduleを作成

NestJSの流儀に従って、まずはModuleを作成します。例としてレシピの一覧が表示できるアプリケーションを想定しています。

```bash
$ nest generate module recipes
CREATE /src/recipes/recipes.module.ts (84 bytes)
UPDATE /src/app.module.ts (325 bytes)
```

app.module.ts に自動的に RecipesModule が追加されるので確認しておきましょう。

```javascript
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 します。

```bash
$ nest generate class recipes/recipe
CREATE /src/recipes/recipe.spec.ts (147 bytes)
CREATE /src/recipes/recipe.ts (23 bytes)
```


```javascript
import { Field, ID, ObjectType } from '@nestjs/graphql';

@ObjectType()
export class Recipe {
  @Field(type => ID)
  id: string;

  @Field()
  title: string;
}
```

# Resolverを作成

最後にクエリの操作を行うリゾルバを作成します。

```bash
$ 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）を実装しています。

```javascript
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 にスキーマが自動的に作成されています。

```javascript

# -----------------------------------------------
# !!! 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-graphql.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163591/b5df3895-ef8d-bc4b-6cfa-c2fa8d9f72d4.gif)


NestJSを使用することで、モデルに対してデコレータを付与するだけでシンプルかつ簡単に実装できました。

NestJSは適切にDIをすることでコードのテスタビリティをあげることができる強力なフレームワークです。
GraphQLサーバを組む場合にも威力を発揮できる可能性があり魅力的ですね。