GraphQL
1. GraphQLとは?
GraphQLは、APIのためのクエリ言語であり、既存のデータに対してそれらのクエリを実行するためのランタイムです。2012年にFacebook(現Meta)によって開発され、2015年に公開されました。
1.1 主な特徴
- クライアント指定のクエリ: 必要なデータだけを正確にリクエストでき、それ以上のデータは取得しません。
- シングルエンドポイント: すべてのリソースに単一のエンドポイントからアクセス可能です。
- 強力な型付け: 明確なスキーマによって、利用可能なデータと操作が定義されます。
- 階層構造: クエリはデータの形状と一致します。
- 自己文書化: スキーマ自体がドキュメントとして機能します。
注意: RESTとは異なり、GraphQLはクライアントが必要なデータを正確に指定できるため、データの「オーバーフェッチ(取得しすぎ)」や「アンダーフェッチ(不足)」を削減できます。
2. Node.jsでGraphQLを始める
2.1 前提条件
- Node.jsがインストールされていること(v14以降を推奨)
- JavaScriptおよびNode.jsに関する基本的な知識
- npmまたはyarnパッケージマネージャー
2.2 ステップ 1:新しいプロジェクトのセットアップ
新しいディレクトリを作成し、Node.jsプロジェクトを初期化します:
mkdir graphql-server
cd graphql-server
npm init -y2.3 ステップ 2:必要なパッケージのインストール
依存関係をインストールします:
npm install express express-graphql graphql各パッケージの役割:
- express: Node.js用のWebフレームワーク
- express-graphql: GraphQL HTTPサーバーを作成するためのミドルウェア
- graphql: GraphQLのJavaScript参照実装
3. 基本的なGraphQLサーバーの作成
3.1 データモデルの定義
server.js ファイルを作成し、GraphQLのスキーマ定義言語(SDL)を使用してデータモデルを定義することから始めます。
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
// サンプルデータ
const books = [
{
id: '1',
title: 'The Great Gatsby',
author: 'F. Scott Fitzgerald',
year: 1925,
genre: 'Novel'
},
{
id: '2',
title: 'To Kill a Mockingbird',
author: 'Harper Lee',
year: 1960,
genre: 'Southern Gothic'
}
];3.2 GraphQLスキーマの定義
server.js にスキーマ定義を追加します:
// GraphQLスキーマ言語を使用してスキーマを定義
const schema = buildSchema(`
# 本(Book)はタイトル、著者、出版年を持ちます
type Book {
id: ID!
title: String!
author: String!
year: Int
genre: String
}
# "Query" 型はすべてのGraphQLクエリのルートです
type Query {
# すべての本を取得
books: [Book!]!
# IDで特定の1冊を取得
book(id: ID!): Book
# タイトルまたは著者で本を検索
searchBooks(query: String!): [Book!]!
}
`);3.3 リゾルバー(Resolvers)の実装
実際のデータを取得するためのリゾルバー関数を追加します:
// スキーマのフィールドに対応するリゾルバーを定義
const root = {
// すべての本を取得するリゾルバー
books: () => books,
// IDで1冊の本を取得するリゾルバー
book: ({ id }) => books.find(book => book.id === id),
// 本を検索するリゾルバー
searchBooks: ({ query }) => {
const searchTerm = query.toLowerCase();
return books.filter(
book =>
book.title.toLowerCase().includes(searchTerm) ||
book.author.toLowerCase().includes(searchTerm)
);
}
};3.4 Expressサーバーのセットアップ
サーバーの設定を完了させます:
// Expressアプリの作成
const app = express();
// GraphQLエンドポイントの設定
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
// テスト用のGraphiQLインターフェースを有効化
graphiql: true,
}));
// サーバーの起動
const PORT = 4000;
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}/graphql`);
});4. GraphQLサーバーの実行とテスト
4.1 サーバーの起動
Node.jsでサーバーを実行します:
node server.jsServer running at http://localhost:4000/graphql というメッセージが表示されるはずです。
4.2 GraphiQLでのテスト
ブラウザで http://localhost:4000/graphql を開き、GraphiQLインターフェースにアクセスします。
クエリ例:すべての本を取得
{
books {
id
title
author
year
}
}クエリ例:特定の1冊を取得
{
book(id: "1") {
title
author
genre
}
}クエリ例:本の検索
{
searchBooks(query: "Gatsby") {
title
author
year
}
}5. Mutation(ミューテーション)のハンドリング
Mutationは、サーバー上のデータを変更するために使用されます。本の追加、更新、削除機能を追加しましょう。
5.1 スキーマの更新
スキーマに Mutation 型を追加します:
const schema = buildSchema(`
# ... (以前の型定義はそのまま) ...
# 本の追加・更新用のインプット型
input BookInput {
title: String
author: String
year: Int
genre: String
}
type Mutation {
# 新しい本を追加
addBook(input: BookInput!): Book!
# 既存の本を更新
updateBook(id: ID!, input: BookInput!): Book
# 本を削除
deleteBook(id: ID!): Boolean
}
`);5.2 Mutationリゾルバーの実装
ルートリゾルバーオブジェクトを更新して、Mutation用のリゾルバーを含めます:
const root = {
// ... (以前のQueryリゾルバーはそのまま) ...
// Mutationリゾルバー
addBook: ({ input }) => {
const newBook = {
id: String(books.length + 1),
...input
}
books.push(newBook);
return newBook;
},
updateBook: ({ id, input }) => {
const bookIndex = books.findIndex(book => book.id === id);
if (bookIndex === -1) return null;
const updatedBook = {
...books[bookIndex],
...input
}
books[bookIndex] = updatedBook;
return updatedBook;
},
deleteBook: ({ id }) => {
const bookIndex = books.findIndex(book => book.id === id);
if (bookIndex === -1) return false;
books.splice(bookIndex, 1);
return true;
}
};5.3 Mutationのテスト
本の追加
mutation {
addBook(input: {
title: "1984"
author: "George Orwell"
year: 1949
genre: "Dystopian"
}) {
id
title
author
}
}本の更新
mutation {
updateBook(
id: "1"
input: { year: 1926 }
) {
title
year
}
}本の削除
mutation {
deleteBook(id: "2")
}6. ベストプラクティス
6.1 エラーハンドリング
リゾルバー内では常に適切にエラーを処理してください。
const root = {
book: ({ id }) => {
const book = books.find(book => book.id === id);
if (!book) {
throw new Error('Book not found');
}
return book;
},
// ... 他のリゾルバー
}6.2 データバリデーション
処理を行う前に、入力データを検証します。
const { GraphQLError } = require('graphql');
const root = {
addBook: ({ input }) => {
if (input.year && (input.year < 0 || input.year > new Date().getFullYear() + 1)) {
throw new GraphQLError('Invalid publication year', {
extensions: { code: 'BAD_USER_INPUT' }
});
}
// ... 残りの処理
}
};6.3 N+1問題
DataLoader を使用して、データベースクエリをバッチ化およびキャッシュします。
npm install dataloaderconst DataLoader = require('dataloader');
// 本のためのローダーを作成
const bookLoader = new DataLoader(async (ids) => {
// 実際のアプリではここでデータベースクエリを実行します
return ids.map(id => books.find(book => book.id === id));
});
const root = {
book: ({ id }) => bookLoader.load(id),
// ... 他のリゾルバー
};7. 次のステップ
- 実際のデータベース(MongoDB、PostgreSQLなど)に接続する
- 認証(Authentication)と認可(Authorization)を実装する
- リアルタイム更新のためのサブスクリプション(Subscriptions)を追加する
- より高度な機能のために Apollo Server を検討する
- マイクロサービスのためのスキーマステッチ(Schema Stitching)やフェデレーション(Federation)を学ぶ
ヒント: 再利用性とセキュリティを高めるために、GraphQLの操作では常に変数(Variables)を使用するようにしましょう。
8. GraphQLのスキーマと型
GraphQLのスキーマは、APIの構造とリクエスト可能なデータの型を定義します。
8.1 型システム
GraphQLは、データの形状を定義するために型システムを使用します。以下は基本的なスカラー型です:
| 型 | 説明 | 例 |
|---|---|---|
| Int | 符号付き32ビット整数 | 42 |
| Float | 符号付きダブル精度浮動小数点数 | 3.14 |
| String | UTF-8 文字列 | "Hello, GraphQL!" |
| Boolean | 真偽値 | true, false |
| ID | 一意の識別子(Stringとしてシリアライズされる) | "5f8a8d8e8f8c8d8b8a8e8f8c" |