graphql について
graphqlは、Node.jsとApollo ServerによるGraphQL API構築を学ぶためのスキルです。スキーマ設計やリゾルバー、データアクセス、セキュリティ、サブスクリプション、性能、テストを基礎から応用まで扱います。
主な機能
- 01GraphQLスキーマとリゾルバーの解説
- 02Apollo Server設定例
- 03DataLoaderによるN+1対策
- 04認証と認可のパターン
- 05サブスクリプションとFederationの解説
- 06テストとエラー処理の例
活用シーン
- 01GraphQL API構築の学習
- 02リアルタイムサブスクリプションの追加
- 03N+1データ取得問題の軽減
- 04GraphQLクエリとミューテーションのテスト
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ pluginagentmarketplace/ custom-plugin-nodejs --skill graphqlインストール後、Claude Code で/graphqlを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/config.yamlschema.jsonreferences/GUIDE.mdPATTERNS.mdscripts/validate.py詳細
Node.js を使用した GraphQL スキル
Apollo Server、型安全なスキーマ、リアルタイムサブスクリプションを活用して、柔軟で効率的な GraphQL API を構築する方法を習得します。
クイックスタート
GraphQL API を 4 ステップで作成します。
- スキーマを定義する - 型、クエリ、ミューテーション
- リゾルバーを作成する - データ取得ロジック
- サーバーをセットアップする - Apollo Server の設定
- データを接続する - データベースとの統合
主な概念
スキーマ定義(SDL)
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String
author: User!
}
type Query {
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
post(id: ID!): Post
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User
deleteUser(id: ID!): Boolean!
}
input CreateUserInput {
name: String!
email: String!
password: String!
}
type Subscription {
postCreated: Post!
}
Apollo Server のセットアップ
const { ApolloServer } = require('@apollo/server');
const { expressMiddleware } = require('@apollo/server/express4');
const { readFileSync } = require('fs');
const typeDefs = readFileSync('./schema.graphql', 'utf-8');
const resolvers = {
Query: {
user: async (_, { id }, { dataSources }) => {
return dataSources.userAPI.getUser(id);
},
users: async (_, { limit = 10, offset = 0 }, { dataSources }) => {
return dataSources.userAPI.getUsers(limit, offset);
}
},
Mutation: {
createUser: async (_, { input }, { dataSources }) => {
return dataSources.userAPI.createUser(input);
}
},
User: {
posts: async (parent, _, { dataSources }) => {
return dataSources.postAPI.getPostsByAuthor(parent.id);
}
}
};
const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
app.use('/graphql', express.json(), expressMiddleware(server, {
context: async ({ req }) => ({
user: req.user,
dataSources: {
userAPI: new UserAPI(),
postAPI: new PostAPI()
}
})
}));
学習ロードマップ
初級(2~3 週間)
- ✅ GraphQL の概念を理解する
- ✅ SDL でスキーマを定義する
- ✅ 基本的なリゾルバーを作成する
- ✅ クエリとミューテーションの基本を学ぶ
中級(4~6 週間)
- ✅ フィールドリゾルバーとリレーションシップ
- ✅ 入力値の検証
- ✅ エラー処理
- ✅ N+1 問題に対する DataLoader
上級(8~10 週間)
- ✅ サブスクリプション(リアルタイム)
- ✅ 認証と認可
- ✅ マイクロサービス向けのフェデレーション
- ✅ パフォーマンスの最適化
N+1 問題に対する DataLoader
const DataLoader = require('dataloader');
const createUserLoader = () =>
new DataLoader(async (userIds) => {
const users = await User.find({ _id: { $in: userIds } });
const userMap = new Map(users.map(u => [u.id, u]));
return userIds.map(id => userMap.get(id));
});
// Use in resolver
User: {
posts: (parent, _, { loaders }) => {
return loaders.userLoader.load(parent.authorId);
}
}
認証と認可
const { shield, rule, allow } = require('graphql-shield');
const isAuthenticated = rule()((parent, args, { user }) => {
return user !== null;
});
const isAdmin = rule()((parent, args, { user }) => {
return user?.role === 'admin';
});
const permissions = shield({
Query: { '*': allow, users: isAuthenticated },
Mutation: {
createPost: isAuthenticated,
deleteUser: isAdmin
}
});
エラー処理
const { ApolloError, UserInputError } = require('apollo-server-express');
class NotFoundError extends ApolloError {
constructor(message) {
super(message, 'NOT_FOUND');
}
}
// In resolvers
const resolvers = {
Query: {
user: async (_, { id }) => {
const user = await User.findById(id);
if (!user) throw new NotFoundError(`User ${id} not found`);
return user;
}
}
};
ユニットテストのテンプレート
const { createTestClient } = require('apollo-server-testing');
describe('GraphQL API', () => {
let query, mutate;
beforeAll(() => {
const server = new ApolloServer({ typeDefs, resolvers });
const testClient = createTestClient(server);
query = testClient.query;
mutate = testClient.mutate;
});
it('should return users list', async () => {
const GET_USERS = `query { users { id name } }`;
const res = await query({ query: GET_USERS });
expect(res.errors).toBeUndefined();
expect(res.data.users).toBeDefined();
});
});
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
| N+1 クエリの問題 | フィールドリゾルバー | DataLoader を使用する |
| クエリが遅い | キャッシュがない | Apollo Cache を実装する |
| 型エラー | スキーマの不一致 | codegen で検証する |
| 認証の回避 | ガードがない | graphql-shield を使用する |
使用する場面
次のような場合に GraphQL を使用します。
- クライアントが柔軟なデータ取得を必要とする
- データのニーズが異なる複数のクライアントがある
- 過剰なデータ取得やデータ不足を避けたい
- リアルタイム更新が必要
- バージョン管理なしで API を進化させたい
関連スキル
- Express REST API(代替アプローチ)
- データベース統合(データソース)
- JWT 認証(API の保護)
リソース
コメント
Markdown 対応