[SAMPLE] GraphQL入門 : RESTの次世代API

GraphQL入門 : RESTの次世代API

はじめに

GraphQLは、Facebookが開発したAPIクエリ言語です。RESTful APIの課題を解決し、より効率的なデータ取得を実現します。この記事では、GraphQLの基本から実践的な使い方まで解説します。

GraphQLとは?

定義: APIのためのクエリ言語とランタイム

特徴

  • クライアントが必要なデータを指定
  • 1回のリクエストで複数のリソース取得
  • 型安全
  • 自己文書化

RESTとの比較

REST APIの課題

Over-fetching(過剰取得):

// ユーザー名だけ欲しいのに全データ取得 GET /users/1 { "id": 1, "name": "田中太郎", "email": "tanaka@example.com", "address": "...", "phone": "...", ... }

Under-fetching(不足取得):

// 複数回リクエストが必要 GET /users/1 GET /users/1/posts GET /users/1/comments

GraphQLの解決策

必要なデータだけ取得:

query { user(id: 1) { name posts { title } } }

レスポンス:

{ "data": { "user": { "name": "田中太郎", "posts": [ { "title": "GraphQL入門" } ] } } }

GraphQLの基本構文

Query(読み取り)

query GetUser { user(id: "1") { id name email posts { title createdAt } } }

Mutation(書き込み)

mutation CreatePost { createPost(input: { title: "新しい記事" content: "内容..." }) { id title createdAt } }

Subscription(リアルタイム)

subscription OnPostCreated { postCreated { id title author { name } } }

スキーマ定義

型定義

type User { id: ID! name: String! email: String! posts: [Post!]! } type Post { id: ID! title: String! content: String! author: User! createdAt: DateTime! } type Query { user(id: ID!): User users: [User!]! post(id: ID!): Post posts: [Post!]! } type Mutation { createUser(input: CreateUserInput!): User! createPost(input: CreatePostInput!): Post! updatePost(id: ID!, input: UpdatePostInput!): Post! deletePost(id: ID!): Boolean! }

スカラー型

  • String: 文字列
  • Int: 整数
  • Float: 浮動小数点数
  • Boolean: 真偽値
  • ID: 一意識別子

修飾子

  • !: 必須(null不可)
  • []: 配列

リゾルバーの実装

Node.js + Apollo Server

const { ApolloServer, gql } = require('apollo-server'); // スキーマ定義 const typeDefs = gql` type User { id: ID! name: String! email: String! } type Query { user(id: ID!): User users: [User!]! } `; // リゾルバー const resolvers = { Query: { user: (parent, { id }, context) => { return context.dataSources.userAPI.getUser(id); }, users: (parent, args, context) => { return context.dataSources.userAPI.getUsers(); }, }, }; // サーバー起動 const server = new ApolloServer({ typeDefs, resolvers, }); server.listen().then(({ url }) => { console.log(`🚀 Server ready at ${url}`); });

フロントエンドでの利用

Apollo Client (React)

import { ApolloClient, InMemoryCache, gql, useQuery } from '@apollo/client'; // クライアント設定 const client = new ApolloClient({ uri: 'http://localhost:4000/graphql', cache: new InMemoryCache(), }); // クエリ定義 const GET_USER = gql` query GetUser($id: ID!) { user(id: $id) { name email posts { title } } } `; // コンポーネント function UserProfile({ userId }) { const { loading, error, data } = useQuery(GET_USER, { variables: { id: userId }, }); if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error.message}</p>; return ( <div> <h1>{data.user.name}</h1> <p>{data.user.email}</p> <h2>Posts</h2> {data.user.posts.map(post => ( <div key={post.title}>{post.title}</div> ))} </div> ); }

ベストプラクティス

1. 命名規則

  • : PascalCase(User, Post
  • フィールド: camelCase(firstName, createdAt
  • クエリ/Mutation: camelCase、動詞から始める

2. ページネーション

type Query { posts( first: Int after: String last: Int before: String ): PostConnection! } type PostConnection { edges: [PostEdge!]! pageInfo: PageInfo! } type PostEdge { node: Post! cursor: String! } type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String }

3. エラーハンドリング

const resolvers = { Query: { user: async (parent, { id }, context) => { try { const user = await context.dataSources.userAPI.getUser(id); if (!user) { throw new Error('User not found'); } return user; } catch (error) { throw new Error(`Failed to fetch user: ${error.message}`); } }, }, };

4. N+1問題の解決(DataLoader)

const DataLoader = require('dataloader'); const userLoader = new DataLoader(async (userIds) => { const users = await Users.findAll({ where: { id: userIds } }); return userIds.map(id => users.find(user => user.id === id)); }); const resolvers = { Post: { author: (post, args, { userLoader }) => { return userLoader.load(post.authorId); }, }, };

セキュリティ

1. 深さ制限

const depthLimit = require('graphql-depth-limit'); const server = new ApolloServer({ typeDefs, resolvers, validationRules: [depthLimit(5)], });

2. クエリ複雑度制限

const { createComplexityLimitRule } = require('graphql-validation-complexity'); const server = new ApolloServer({ typeDefs, resolvers, validationRules: [ createComplexityLimitRule(1000), ], });

3. 認証・認可

const resolvers = { Query: { me: (parent, args, context) => { if (!context.user) { throw new AuthenticationError('Not authenticated'); } return context.user; }, }, Mutation: { deletePost: (parent, { id }, context) => { if (!context.user) { throw new AuthenticationError('Not authenticated'); } const post = getPost(id); if (post.authorId !== context.user.id) { throw new ForbiddenError('Not authorized'); } return deletePost(id); }, }, };

まとめ

GraphQLは、RESTの課題を解決する強力なAPIクエリ言語です。

メリット:

  • 必要なデータだけ取得
  • 1回のリクエストで完結
  • 型安全
  • 自己文書化

デメリット:

  • 学習コスト
  • キャッシュが複雑
  • N+1問題への対応必要

おすすめのケース:

  • モバイルアプリ(通信量削減)
  • 複雑なデータ構造
  • 頻繁にAPIが変わる

まずは小さなプロジェクトから始めて、GraphQLの恩恵を実感してみてください。


次に読むべき記事:

  • [[Apollo Server完全ガイド]]
  • [[GraphQLのパフォーマンス最適化]]
  • [[RESTからGraphQLへの移行戦略]]
0
0
0
0
投稿
0
フォロワー
0
いいね

プロパティ

ページ
ガジェットストレージ
ラリー・ペイジ