NestJSとは
TypeScriptを用いてAPIを構築したいときのフレームワークの一つ。
何もせずに利用し始めるとREST APIが使えるが、
GraphQLのライブラリをいれることによりGraphQLのエンドポイントを作ることが可能。
今回やること
NestJSを用いて、GraphQLが扱えるAPIを構築する。
参考
公式サイトを参考に進める。
サンプルAPI構築
①インストール
公式サイトにあるように、nestコマンドをインストールして
それを利用することでサンプルAPIを簡単に作成できる。
$ npm i -g @nestjs/cli
$ nest new project-name
プロジェクトが作成されたら、プロジェクトのルートで必要なパッケージのインストールを行う。
(今回はyarnの利用を選択した)
$ yarn install
②API起動
READMEに記載の通り、以下のコマンドで起動することができる。
$ yarn run start
yarn run v1.22.22
$ nest start
[Nest] 46878 - 2024/06/23 12:34:41 LOG [NestFactory] Starting Nest application...
[Nest] 46878 - 2024/06/23 12:34:41 LOG [InstanceLoader] AppModule dependencies initialized +11ms
[Nest] 46878 - 2024/06/23 12:34:41 LOG [RoutesResolver] AppController {/}: +6ms
[Nest] 46878 - 2024/06/23 12:34:41 LOG [RouterExplorer] Mapped {/, GET} route +1ms
[Nest] 46878 - 2024/06/23 12:34:41 LOG [NestApplication] Nest application successfully started +4ms
localhost:3000
にアクセスすると、「Hello World!」が返却される。
ここまでで、一般的なAPIを立ち上げることができた。
③ディレクトリ構成
srcディレクトリの構成は以下のようになっている。
$ tree src
src
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
└── main.ts
1 directory, 5 files
ファイル名 | 概要 |
app.controller.ts | APIにおける一般的なController。 受け付けるメソッドやパスなどを書く。 |
app.service.ts | APIにおける一般的なService。 ロジックなどを書く。 |
app.module.ts | NestJSはmoduleという単位で機能を集約する。 これはアプリケーション全体を取りまとめるmodule。 |
GraphQLを利用する
ここから、GraphQLを利用できるようにしていく。
参考にする公式サイトは以下の通り。
- コードを書いて、そこからスキーマを生成していく方法
- スキーマをベースに実装していく方法
という2通りの方法があるが、今回は1の方法で実装を進める。
①必要なパッケージをインストールする
$ yarn add @nestjs/graphql @nestjs/apollo @apollo/server graphql
②moduleを作成する
moduleとは、機能をひとまとめにする単位のこと。
ここから、Authorというオブジェクトを作成し、それをGraphQLで提供する実装をしていくが
このAuthorに関する実装をひとまとめにしておく場所になる。
自動生成してくれるコマンドがあるので、こちらを利用して自動生成する。
$ nest g module author
authorというディレクトリが作成され、author.module.ts
が生成されていることが確認できる。
src
├── author
│ └── author.module.ts
③modelを作成する
moduleを作成するとauthorディレクトリが作成されるので、
その中にAuthorオブジェクトのmodelを作成してく。
これが、レスポンスオブジェクトのベースの形になる。
src/author/author.model.ts
import { Field, Int, ObjectType } from '@nestjs/graphql';
// オブジェクト全体に対してdescriptionを記載することもできる
@ObjectType({ description: 'Authorオブジェクト' })
export class Author {
// GraphQLには数値型はIntとFloatがあるので、Int利用を明記する
@Field((type) => Int, { description: 'ID' })
id: number;
// descriptionにより、フィールドに関する説明を記載できる
@Field((type) => String, { description: '名前' })
name: string;
// nullableの場合もここで指定する
@Field((type) => Int, { nullable: true, description: '年齢' })
age?: number;
}
@ObjetcType()
を付与したクラスを作成し、そこにフィールドを記載する。
各フィールドには@Field()
デコレータを用いて、フィールドに関する付加情報を記述する。
④resolverを作成する
resolverにはクエリに関する振る舞いを定義する。
レスポンスはどんな形か、リクエストできるパラメータは何か、どんなクエリを受け付けているかなどを記載する。
src/author/author.resolver.ts
import { Args, Int, Query, Resolver } from '@nestjs/graphql';
import { Author } from './author.model';
// Authorに相当するスキーマを返却することを宣言している
@Resolver((of) => Author)
export class AuthorResolver {
// わかりやすさのため、データをベタ書き
private authors: Author[] = [
{
id: 1,
name: 'hoge',
age: 10,
},
{
id: 2,
name: 'fuga',
},
{
id: 3,
name: 'piyo',
age: 20,
},
];
// Authorの配列を返却することを宣言している
// descriptionにより説明文も記載可能
@Query((returns) => [Author], { description: 'Authorを全て取得する' })
async getAuthor() {
return this.authors;
}
// Authorを単体で返却することを宣言している
// nullableのオプションをtrueに設定しているため、Authorオブジェクトもしくはnullが返却されることがわかる
@Query((returns) => Author, {
nullable: true,
description: 'ID指定でAuthorを取得する',
})
// `id`というInt型の引数をリクエスト時に指定することで、idを使った絞り込みが行えることを明示している
async findAuthor(
@Args('id', { type: () => Int, description: '絞り込み用のID' }) id: number,
) {
return this.authors.find((item) => item.id === id);
}
}
⑤moduleにresolverを記載する
作成したresolverは、moduleに記載して、moduleとしてまとめる対象としておく。
src/author/author.module.ts
import { Module } from '@nestjs/common';
import { AuthorResolver } from './author.resolver';
@Module({
providers: [AuthorResolver], // ここに追加
})
export class AuthorModule {}
⑥ルート要素のmoduleに追加
全体を取りまとめているapp.module.ts
に
- GraphQLを利用すること
- 先ほど作成したAuthorに関するmodule
を明記する。
app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';
import { AuthorModule } from './author/author.module';
import { ApolloDriver } from '@nestjs/apollo';
@Module({
imports: [
// GraphQLを利用する際はこれを追加
GraphQLModule.forRoot({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'), // コードファーストで実装しているので、スキーマは自動生成される
sortSchema: true,
}),
AuthorModule, // 作成したmoduleをここに記載
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
最終的なディレクトリ構成は以下の通り。
$ tree src
src
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
├── author
│ ├── author.model.ts
│ ├── author.module.ts
│ ├── author.resolver.spec.ts
│ └── author.resolver.ts
└── main.ts
動作確認
①サーバー起動
以下のコマンドを実行することで、サーバーを起動できる。
起動した際に、src/schema.gql
というファイルが自動生成されていればOK。
$ yarn run start:dev
src/schema.gql
# ------------------------------------------------------
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------
"""Authorオブジェクト"""
type Author {
"""年齢"""
age: Int
"""ID"""
id: Int!
"""名前"""
name: String!
}
type Query {
"""ID指定でAuthorを取得する"""
findAuthor(
"""絞り込み用のID"""
id: Int!
): Author
"""Authorを全て取得する"""
getAuthor: [Author!]!
}
descriptionに記載した内容が反映されつつ、GraphQLのスキーマが自動生成されていることがわかる。
②playground確認
http://localhost:3000/graphql
にアクセスすると、GraphQLのplaygroundを確認することができる。
SCHEMAタブにはスキーマに関する情報が出力されており、GraphQLのクエリも実行することができている。
descriptionを記載しておくことで、スキーマの説明が分かりやすくなるので
できる限り記載するクセを身に着けておくと良い。
まとめ
- NestJSはAPIを構築するためのフレームワーク
- NestJSは作ったものをmoduleという単位でまとめておくのがルール
- GraphQLのライブラリを組み込むことで、GraphQLに対応させることができる
- GraphQLではObjectTypeでデータ型を定義し、Resolverでクエリを処理する