今回は、Node.js + TypeScriptによるGraphQL入門ということで、ApolloServerを使って超絶簡単なAPIを実装してきます。業務で使用することになったため、そのキャッチアップも兼ねて備忘録としてまとめていきます。
GraphQLについて
GraphQLとは
GraphQLはクライアントとサーバー間のデータ通信を効率的に行うためのクエリ言語。(REST APIの課題を解決するために、Facebook(Meta)によって開発された)
クライアントが必要なデータを明示的に指定し、サーバーはその要求に応じてデータを提供する。そのため、REST APIと比べて、冗長な通信や過剰なデータ取得を防ぐことが可能。また、1つのエンドポイントで複数のデータを取得できるため、フロントエンドとバックエンドの連携がスムーズとなる。スキーマとリゾルバという概念があり、これらを組み合わることにより、柔軟でパワフルなデータ取得機構を実現しAPI開発の効率性とパフォーマンス向上に貢献する。
REST APIとの違い
比較項目 | GraphQL | REST API |
---|---|---|
データ取得の柔軟性 | クライアントが必要なデータをリクエストし、必要なデータのみを受け取る | 固定されたエンドポイントから全てのデータを取得する |
データの効率的な取得 | 1つのリクエストで複数のデータを取得できる | 複数のエンドポイントへのリクエストが必要 |
スキーマと型の定義 | 明示的なスキーマと型を持ち、APIの構造を公開 | APIドキュメントやドメイン知識に依存 |
バージョン管理 | スキーマの追加でバージョン管理が容易 | エンドポイントの変更が必要で管理が煩雑 |
ツールとエコシステム | 比較的新しい技術であるため、ツールやライブラリの不足 | 選択肢は多いが標準化された仕様がない |
Apollo Serverについて
Apollo Serverとは
Apollo Serverは、GraphQLサーバーの構築に特化したツールです。スキーマファーストの開発アプローチを採用し、柔軟なAPI設計を可能にすることが特徴。豊富な統合オプションを提供し、データ管理や認証・認可などの機能を簡単に実装できる。Apollo Serverを使用することで、効率的なGraphQL APIの構築とクライアントとのスムーズなデータ通信が実現できます。
特徴
- スキーマファーストの開発: スキーマを中心にAPIを設計し、柔軟で一貫性のあるGraphQL APIを構築。
- プラットフォームとフレームワークの統合: ExpressやFastify、Koaなどのフレームワークとのシームレスな統合が可能。
- データ管理の柔軟性: データソースの統合やバッチリクエスト、キャッシュの管理など、高度なデータ要求の制御が可能。
- 拡張性とエコシステム: プラグインやミドルウェアを活用して機能を拡張し、豊富なエコシステムを利用。
- パフォーマンスとセキュリティ: クエリの最適化、クエリの監査、認証・認可の仕組みなど、パフォーマンスとセキュリティに焦点を当てた機能を提供。
実装の流れ
前回の記事を参考にNode.js + TypeScriptの実行環境を構築
GraphQLやApolloServerのパッケージを追加
どういうデータのやり取りをするか定義
APIサーバーとして必要な設定を追加し、起動処理を実装
コンソール画面より正常に動作しているか確認
開発環境
- Node.js:
v18.15.0
- npm:
v9.5.0
- TypeScript:
v5.0.2
- graphql:
v16.7.1
- @apollo/server:
v4.7.4
- OS:
MacOS Monterey
構築手順
実行環境の構築
まずはNode.js + TypeScriptで実行できる環境を構築。
構築方法は以下をベースに実施しましたので、まだ構築できていない方は参考にしてください。
{
"compilerOptions": {
"target": "es2020",
"module": "esnext", // "commonjs"から"esnext"に変更
"moduleResolution": "node",
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"include": ["./src/**/*.ts"]
}
{
"name": "ts-node-graphql",
"version": "1.0.0",
"description": "",
"main": "index.js",
"volta": {
"node": "18.15.0"
},
"type": "module", //追加
"scripts": {
"build": "npx tsc",
"build:watch": "npx tsc --watch",
"start:dev": "nodemon dist/index.js"
},
//略
}
必要なライブラリのインストール
今回は下記ライブラリが必要となるためインストールを行っていきます。
- graphql:
v16.7.1
- @apollo/server:
v4.7.4
$ npm install @apollo/server graphql
GraphQLスキーマとリゾルバを定義
GraphQLスキーマ
すべてのGraphQLサーバー(Apollo Serverを含む)はスキーマを使用して、クライアントがクエリできるデータの構造を定義する必要がある。
このサンプルでは、公式に合わせてtitle
とauthor
を持つBook
コレクションをクエリするためのサーバーを作成していきます。
const typeDefs = `#graphql
type Book {
title: String! //感嘆符(!)を入れることでNullを許可しないようにできる
author: String!
}
type Query {
books: [Book!]! //配列かつ各要素はBookオブジェクトであり、Nullではない。空配列はOK。
}
`;
このスキーマを定義することで、クライアント側からbooksというクエリが実行されると、Bookの配列が返ってくることを定義している。
※ スキーマについても整理したいですが、それだけで1記事書けそうなので別でまとめたいと思います
リゾルバ
Apollo Serverは、スキーマ内のすべてのフィールドのデータに対してどう処理するのかを把握している必要があります。これを解決するためにリゾルバを使用する。
リゾルバは、スキーマ内の1つのフィールドのデータを入力する役割を持つ関数となり、バックエンドのデータベースやサードパーティの API からデータを取得するなど、任意の方法でデータを扱うことができる。
しかし、今回はデータベースや外部APIも使用しないため、ハードコーディングでBookオブジェクトを持つ配列を定義しておきます。
const books = [
{
title: 'プロを目指す人のためのTypeScript入門',
author: '鈴木 僚太',
},
{
title: '良いコード/悪いコードで学ぶ設計入門',
author: '仙塲 大也',
},
];
const resolvers = {
Query: {
books: () => books,
},
};
ApolloServerの構築と起動
最後にApolloServer
のインスタンスを作成(初期化)し、それをもとにサーバー起動すれば準備完了です。
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
//略
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
});
console.log(`🚀 Server ready at: ${url}`);
ApolloServer
を初期化する際に、スキーマ(typeDefs)とリゾルバ(resolvers)を引数に渡してあげるstartStandaloneServer
をポート4000番で起動- 起動時のURL情報を取得してログ出力
動作検証
最終的なコード
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
const books = [
{
title: 'プロを目指す人のためのTypeScript入門',
author: '鈴木 僚太',
},
{
title: '良いコード/悪いコードで学ぶ設計入門',
author: '仙塲 大也',
},
];
// Adding #graphql to the beginning of a template literal provides GraphQL syntax highlighting in supporting IDEs.
const typeDefs = `#graphql
type Book {
title: String!
author: String!
}
type Query {
books: [Book!]!
}
`;
const resolvers = {
Query: {
books: () => books,
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
});
console.log(`🚀 Server ready at: ${url}`);
動作検証
ここまで実装できたら実際にサーバーを起動し、GraphQLを実行できるApollo Sandboxを開いていきます。
$ npm run start:dev
[nodemon] restarting due to changes...
[nodemon] starting `node dist/index.js`
🚀 Server ready at: http://localhost:4000/
http://localhost:4000/にアクセスすると以下のような画面が開きます。
クエリの実行やスキーマの確認などができるコンソール画面になっているようです。REST APIで使っていたPostmanと同じですね。
実際にクエリを実行してみると、期待するレスポンスが返ってきているはずです。
query Query {
books {
title
}
}
試しにauthor
も追加して取得してみます。
query Query {
books {
title
author
}
}
無事に定義した通りにレスポンスを受け取ることができました。
ハードコーディングしていた部分の値を変えてみたりすれば、レスポンス結果も変わるはずです。
まとめ
今回はNode.js + TypeScriptによるGraphQL入門ということで、ApolloServerを使って超絶簡単なAPIを実装してみました。あくまでも公式ドキュメント通りに実装しただけなので、ここからデータベースや外部APIからデータを取得できるように別途記事にまとめていきたいと思います。