Help us understand the problem. What is going on with this article?

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

image.png
NestJSは、TypeScriptで記述するバックエンドアプリケーションフレームワークです。デフォルトで DI(Dependency Injection) の仕組みをサポートしており、テスト可能な構成を簡単に作ることができる特徴があります。
今回の記事ではNestJSを使用して最もシンプルなGraphQLサーバを構築します。
↓完成イメージ
nestjs-graphql.gif

GraphQLの基本

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

image.png

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の形式になっていますね。
image.png

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

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

$ npm i --save @nestjs/graphql \
               apollo-server-express \
               graphql-tools \
               graphql \
               type-graphql

REST API用に作られていた app.module.ts を書き換えましょう。
Controller, Service の箇所を GraphQLModule として書き換えました。
.forRoot() メソッドで playground: true を宣言することで ブラウザ(http://localhost:3000/graphql)で GraphQL IDEを表示できます。autoSchemaFile は自動的に生成されたスキーマが作成されるパスを示しています

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

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 を作成します。
type-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 'type-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;
  }
}

ここまででディレクトリ構成は以下のようになっています。

src$ 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-graphql.gif

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

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

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away