Nest.jsでGraphQL APIを構築するには?
この記事では、NestJSのクリーンなアーキテクチャと型安全性を利用して、NestJSでGraphQL APIを構築する方法を学びます。
NestJSは、型安全性と厳格なアーキテクチャパターンを提供することで、スケーラブルなエンタープライズグレードのバックエンドアプリケーションの作成を支援するNodeJSフレームワークです。
GraphQLはアプリケーション・プログラミング・インターフェース(API)のためのクエリ言語で、RESTに代わるものとして広く使用されています。その速度と安定性により、クエリに対して要求されたデータを正確に応答することで実現されています。このGraphQL機能は、RESTが抱えるオーバーフェッチとアンダーフェッチの問題を解決します。
Contents
開発環境のセットアップ
Nest.js CLIがシステムにグローバルにインストールされていない場合は、以下のコマンドを実行してインストールしてください:
npm i -g @nestjs/cli
Nest.js CLIには、開発プロセスを容易にするための定型コードを生成するためのコマンドがいくつかあります。
次に、以下のコマンドを実行してプロジェクトを作成し、必要なスターター・ファイルを生成する:
nest new graphql-api
次に、以下のコマンドを実行してプロジェクト・ディレクトリにcdする:
cd graphql-api
次に、以下のコマンドを実行して、必要な依存関係をインストールする:
npm i @nestjs/graphql @nestjs/apollo graphql apollo-server-express
上記でインストールされた依存関係は以下の通り:
nest/graphql
nestjs/apollo
graphql
apollo-server-express
次に、以下のコマンドを実行し、TypeORMパッケージとsqliteデータベースドライバをインストールします:
npm install @nestjs/typeorm typeorm sqlite3
データベースへの接続、読み込み、書き込みを行うには、上記でインストールしたパッケージが必要です。
Nest.jsでGraphQLを設定する
Nest.jsは、GraphQL APIを作成する方法として、コードファーストとスキーマファーストの2つを提供している。
コードファーストのアプローチでは、TypeScriptのデコレーターとクラスを使用して、対応するGraphQLスキーマを生成する。
スキーマファーストのアプローチでは、GraphQL Schema Definition Language(SDL)ファイルを使用し、Nest.jsはGraphQL Schemaに基づくクラスまたはインターフェイスを使用してTypeScript定義を生成します。
このチュートリアルでは、異なる言語間でのコンテキストの切り替えを避けるために、コード・ファースト・アプローチを使用することを特徴とする。
コードファーストのアプローチを使用するには、アプリケーションのエントリーファイルであるapp.module.tsに
移動し、@nestjs/graphqlから
GraphQLModuleを
インポートします。このように:
import { GraphQLModule } from '@nestjs/graphql';
次に、imports
配列でGraphQLModuleの
forRoot
メソッドを呼び出します。このメソッドは引数として設定オブジェクトを取ります。設定オプションは、基礎となるGraphQLドライバを介して渡されます(このチュートリアルでは、Apolloドライバを使用します)。設定オブジェクトのautoSchemaFile
プロパティをschema.gqlに
設定します。このように:
//app.module.ts
GraphQLModule.forRoot({ autoSchemaFile: 'schema.gql' }),
autoSchemaFile
プロパティは、自動生成されたスキーマが保存されるファイルパスです。
データベースへの接続
アプリケーションをデータベースに接続するには、app.module.ts
ファイルに移動し、@nestjs/typeorm
からTypeOrmModule を
インポートします。
こんな感じだ:
import { TypeOrmModule } from '@nestjs/typeorm';
次に、以下のコードブロックをimports
配列に追加する:
TypeOrmModule.forRoot({
type: 'sqlite',
database: ':memory:',
entities: ['dist/**/*.entity{.ts,.js}'],
synchronize: true,
}),
上記のコードブロックは、アプリケーションとSqliteデータベースとの間にデータベース接続を作成し、アプリケーション内のすべてのモジュールでその接続を共有します。
リソースの生成
次に、以下のコマンドを実行して、コードを整理するためのモジュールを作成する:
nest g module blog
上記のコマンドは、アプリケーション用のブログ・モジュールを生成する。
次に、以下のコマンドを実行してサービスを作成する:
nest g service blog
上記のコード・ブロックは、アプリケーションにサービスを生成して登録します。このサービスには、アプリケーションのすべてのビジネスロジックが含まれ、処理されます。
次に、以下のコマンドを実行してリゾルバを作成する:
nest g resolver
上記のコードブロックは、リゾルバを生成してアプリケーションに登録します。リゾルバは、GraphQLクエリーと変異をデータに変換する命令を指定し、スキーマで指定されたデータの形状を同期または非同期で返します。
エンティティの作成
エンティティとは、データベースにデータがどのように格納されるかを指定するフィールドの集まりである。
Entityを作成するには、モジュールフォルダ(blog)に、.entity.tsの
規約に従ってEntityファイルを作成します(例:blog.entity.ts
)。
エンティティファイルに、typeormから
Column
、Entity
、PrimaryGeneratedColumnを
インポートします。このように:
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
Column
デコレータは、アノテーションしたフィールドをデータベースのカラムとして追加します。Entity
デコレータは、アノテーションするクラスをエンティティとしてマークします。PrimaryGeneratedColumn
デコレータは、 アノテーションしたプロパティをテーブルの一意な識別子としてマークし、 それを自動的に生成します。
次に、Blog
というクラスを作成してエクスポートする。そして、このクラスをエンティティとしてマークするために、Entity
デコレーターでアノテーションする。このように:
@Entity()
export class Blog {}
このチュートリアルではブログAPIを使用するので、クラスにid、title、body、author、dateプロパティを追加します。そして、id
プロパティを除く各プロパティにColumn
デコレーターをアノテーションします。id
プロパティにPrimaryGeneratedColumn
デコレータをアノテーションします。
こんな感じだ:
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Blog {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
body: string;
@Column()
author: string;
@Column()
date: string;
}
オブジェクト・タイプの作成
オブジェクトタイプは、クライアントが対話する必要があるオブジェクトを表すGraphQLスキーマ定義です。例えば、ブログAPIを構築する場合、ブログのスキーマ/エンティティをTypeScriptクラスで定義し、各フィールドに適切なデコレーターをアノテーションする必要があります。
次に、エンティティファイルで、@nestjs/graphqlから
Int
型とFieldと
ObjectType
デコレータをインポートします。このようにします:
import { Field, Int, ObjectType } from '@nestjs/graphql';
Field
デコレーターは、特定のクラスのプロパティーを GraphQL フィールドとしてマークします。このデコレーターでアノテーションされたプロパティのみがスキーマで定義されます。オプションの戻り値型関数と設定オブジェクトを引数として受け取ります。
Int
型は、TypeScriptとGraphQLの型システムの間の潜在的な曖昧さを解消するために必要です。これは、整数または浮動小数点数の値を持つプロパティにのみ必要です。
ObjectType
デコレーターは、クラスをGraphQL型としてマークします。
次に、BlogクラスをObjectType
デコレーターでアノテーションし、GraphQLタイプとしてマークします。次のように:
@ObjectType()
export class Blog {}
最後に、型の必須プロパティをクラス内部で指定し、それぞれにField
デコレーターのアノテーションを付けます。例えば
//blog.entity.ts
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
@ObjectType()
export class Blog {
@PrimaryGeneratedColumn()
@Field((type) => Int)
id: number;
@Column()
@Field()
title: string;
@Column()
@Field()
body: string;
@Column()
@Field()
author: string;
@Column()
@Field()
date: string;
}
完成したオブジェクト・タイプは、上のコード・ブロックのようになるはずだ。
上のコードブロックでは、TypeScriptとGraphQLの型システムのあいまいさを避けるために、数値
型のidが
明示的に整数として指定されていることに注目してほしい。
最後に、Entityをアプリケーションに注入して、アプリケーションとデータベースの間にアクセスレイヤーを作成します。
これを実現するには、blog.module.ts
ファイルに移動し、@nestjs/typeorm
からTypeOrmModule を
インポートし、Blog エンティティをインポートします。
次に、以下のコード・ブロックをモジュール・デコレーターに追加します:
imports: [TypeOrmModule.forFeature([Blog])],
上のコードブロックは、アプリケーションとデータベースの間のアクセスレイヤーとして機能するリポジトリを作成する。
サービスの作成
先に生成したサービスファイルblog.service.tsに
移動し、@nestjs/typeormから
InjectRepositoryと
オブジェクトタイプをインポートします。
こんな感じだ:
import { InjectRepository } from '@nestjs/typeorm';
import { Blog } from './blog.entity';
次に、以下のコードブロックをBlogService
クラスに追加して、リポジトリをインジェクトします:
constructor(
@InjectRepository(Blog)
private blogRepository: Repository<Blog>,
) {}
次に、以下のコードブロックをサービスクラスに追加し、データベース内のすべてのブログを検索するロジックを実装します:
async getAll(): Promise<Blog[]> {
return await this.blogRepository.find();
}
次に、新しいブログをデータベースに追加するロジックを実装する必要がありますが、その前にInput Typeを作成する必要があります。
入力タイプを作成する
複雑なオブジェクトを受け取る突然変異の入力タイプを作成するには、ブログ・フォルダーに
dtos
フォルダーを作成してください。次に、dtos
フォルダ内に、新しいファイルcreate-blog.input.tsを
作成します。
create-blog.input.ts
ファイルで、Field
デコレーターとInputType
デコレーターをインポートします。このように:
import { Field, InputType } from '@nestjs/graphql';
InputType
デコレーターは、クラスを GraphQL 入力タイプとしてマークします。
次に、BlogInput
クラスを作成し、エクスポートする。そして、そのクラスにInputType
デコレーターをアノテーションする。次のように:
@InputType()
export class BlogInput {}
最後に、必要なブログ・プロパティをすべて追加し、フィールド・デコレーターで
アノテーションします。
こんな感じだ:
//create-blog.input.ts
import { Field, InputType } from '@nestjs/graphql';
@InputType()
export class BlogInput {
@Field()
id: number;
@Field()
title: string;
@Field()
body: string;
@Field()
author: string;
@Field()
date: string;
}
完成した入力タイプは、上のコードブロックのようになるはずだ。
次に、入力タイプをサービスファイルにインポートし、以下のコードブロックをサービスクラスに追加して、データベースにブログを追加するロジックを実装します:
async createBlog(blogInput: BlogInput): Promise<Blog> {
const newBlog = this.blogRepository.create(blogInput);
return this.blogRepository.save(newBlog);
}
入力タイプは、通常のNest.jsデータ転送オブジェクトが適用されるのと同じように、入力データの検証メカニズムとして適用されます。dtosのように、class-validator
パッケージを使用してデータに対して追加の検証を実行することもできます。
リゾルバの作成
先に生成したResolverファイルblog.resolver.tsに
移動し、エンティティ、入力タイプ、サービスをインポートします。このように:
import { Blog } from './blog.entity';
import { BlogService } from './blog.service';
import { BlogInput } from './dto/create-blog.input';
次に、以下のコードブロックをリゾルバクラスに追加して、 サービスをリゾルバに注入します:
constructor(private blogService: BlogService) {}
次に、@nestjs/graphqlから
Args
、Mutation
、Query
デコレータをインポートします。
こんな感じだ:
import { Args, Mutation, Query } from '@nestjs/graphql';
Args
デコレーターは、基本プラットフォームから arguments オブジェクトを取り出し、すべての引数あるいは指定した引数の値をデコレートしたパラメーターに設定します。
Mutation
デコレーターは、ルートを変異として指定します。
Query
デコレータはルートをクエリとして指定します。
次に、以下の関数をResolver
デコレーターに渡してオブジェクトの型を指定します:
(of) => Blog
次に、getAll
メソッドを解決するために、以下のコードブロックをResolverクラスに追加します。
@Query((type) => [Blog])
async getAllBlogs() {
return this.blogService.getAll();
}
最後に、以下のコード・ブロックをResolverクラスに追加して、createBlog
メソッドを解決します。
@Mutation((returns) => Blog)
createBlog(@Args('blogInput') blogInput: BlogInput): Promise<Blog> {
return this.blogService.createBlog(blogInput);
}
注意:各デコレーターの戻り値の型関数を必ず指定してください。
完成したResolverクラスは、以下のコード・ブロックのようになるはずです:
@Resolver((of) => Blog)
export class BlogResolver {
constructor(private blogService: BlogService) {}
@Query((type) => [Blog])
async getAllBlogs() {
return this.blogService.getAll();
}
@Mutation((returns) => Blog)
createBlog(@Args('blogInput') blogInput: BlogInput): Promise<Blog> {
return this.blogService.createBlog(blogInput);
}
}
GraphQL Playgroundを使用してAPIをテストする
デフォルトで利用可能なGraphQLプレイグラウンドを使用してアプリケーションをテストするには、以下のコマンドを実行し、http://localhost:3000/graphql。
npm start
次に、下の画像に示すように、クエリーと突然変異をテストできるプレイグラウンドが表示されるはずだ。
データベースからすべてのブログを取得するには、以下のクエリを実行します:
query getAll{
getAllBlogs {
id,
title,
body,
author,
date
}
}
例を下の画像に示す:
データベースにブログを追加するには、以下の変異を実行する:
mutation createBlog {
createBlog (blogInput: {
id: 1,
title: "GraphQL is great",
body: "GraphQL APIs are faster than REST APIs",
author: "David Ekete",
date: "01-02-03"
}) {
id,
title,
body,
author,
date,
}
}
例を下の画像に示す:
Back4appとGraphQL
データ取得制御など、GraphQL には REST よりも多くの利点があるため、Back4app プラットフォームでは、プラットフォーム上で実行される解析アプリケーションに統合された GraphQL API を提供しています。
この統合により、簡単なGraphQLクエリや変異を書くだけで、サーバーのバックエンドの健全性をチェックしたり、データベース上でCRUD(Create、Read、Update、Delete)操作を実行したり、その他多くのことができるようになります。
例えば、GraphQL APIを使用してバックエンドサーバーの健全性を確認するには、以下のクエリーを実行します:
query Health {
health
}
上のクエリーはこう返す:
{
"data": {
"health": true
}
}
GraphQL 機能を開始するには、Parse ダッシュボードに移動し、アプリを選択してクリックします。アプリの UI で[Core]を選択してクリックし、[API console]を選択してクリックし、[GraphQL console]を選択します。
上記の指示に従うと、GraphQLコンソールが開き、開発で使用したGraphQLプレイグラウンドと同様のクエリやミューテーションを実行することができます。Back4appのGraphQL機能については、Back4appのドキュメントを参照してください。
結論
この記事では、Nest.jsでGraphQL APIを作成し、GraphQL playgroundを使ってAPIをテストする方法を取り上げた。GraphQL APIには、REST APIに比べていくつかの利点があります。
しかし、これらはすべてのアプリケーションに理想的というわけではありません。GraphQLとRESTの長所と短所を考慮した上で、どちらがあなたのアプリケーションに適しているかを選ぶようにしてください。
GraphQLやRESTの選択に関わらず、アプリケーションをデプロイする必要があります。Back4appはオープンソースの技術をベースにしたローコードBaaSで、再利用可能なテンプレートを使って繰り返しの作業をなくし、バックエンドのインフラストラクチャの大部分を管理することで、バックエンド構築のプロセスを簡素化します。
Back4appの特徴には、リアルタイムデータベース、クラウド機能、ネイティブAPIとSDK、完全なユーザー管理システムなどがあります。
このプラットフォームには、料金を支払うことなくプラットフォームを探索できる無料ティアもある。
ハッピービルディング
よくあるご質問
GraphQL とは何ですか?
GraphQL は、2012 年に Facebook によって作成された、API 用のオープンソースのデータ クエリおよび操作言語です。
Nest.JS とは何ですか?
Nest.JS は、効率的でスケーラブルなサーバー側アプリケーションを構築するためのオープンソースの Node.js フレームワークです。
Nest.JS を使用して GraphQL API を作成する方法は?
– Nest.js で GraphQL を構成する
– データベースに接続する
– リソースを生成する
– エンティティを作成する
– オブジェクト タイプを作成する
– サービスを作成する