GraphQLサーバー構築の概要
GraphQLはクライアントが必要なデータのみを指定して取得できる柔軟なAPIクエリ言語です。REST APIと比較してオーバーフェッチ・アンダーフェッチを防ぎ、フロントエンド開発の効率を大幅に向上させます。
Apollo Server(Node.js)の基本構築
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { makeExecutableSchema } from '@graphql-tools/schema';
const typeDefs = `#graphql
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
body: String!
author: User!
createdAt: String!
}
type Query {
users(page: Int, limit: Int): UserConnection!
user(id: ID!): User
posts: [Post!]!
}
type UserConnection {
nodes: [User!]!
totalCount: Int!
pageInfo: PageInfo!
}
type PageInfo {
hasNextPage: Boolean!
endCursor: String
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
}
input CreateUserInput {
name: String!
email: String!
}
input UpdateUserInput {
name: String
email: String
}
`;
const resolvers = {
Query: {
users: async (_, { page = 1, limit = 10 }, { db }) => {
const total = await db.users.count();
const users = await db.users.findMany({
skip: (page - 1) * limit,
take: limit
});
return {
nodes: users,
totalCount: total,
pageInfo: { hasNextPage: page * limit < total }
};
},
user: async (_, { id }, { db }) => db.users.findUnique({ where: { id } })
},
Mutation: {
createUser: async (_, { input }, { db }) => {
return db.users.create({ data: input });
}
}
};
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
context: async ({ req }) => ({
db: prisma,
user: await authenticate(req.headers.authorization)
}),
listen: { port: 4000 }
});
DataLoaderでN+1問題を解決する
import DataLoader from 'dataloader';
// ユーザーをバッチで取得するDataLoader
const createUserLoader = (db) => new DataLoader(async (userIds) => {
const users = await db.users.findMany({
where: { id: { in: userIds as string[] } }
});
// userIdsの順序に合わせてマッピング
return userIds.map(id => users.find(u => u.id === id) || null);
});
// コンテキストにDataLoaderを追加
context: async () => ({
db: prisma,
loaders: {
user: createUserLoader(prisma)
}
})
// リゾルバーでDataLoaderを使用
Post: {
author: async (post, _, { loaders }) => {
return loaders.user.load(post.authorId); // 自動的にバッチ処理
}
}
Hasuraで自動GraphQL生成
HasuraはDockerで数分で起動し、PostgreSQLのテーブルを選択するだけでCRUD操作・フィルタリング・ページネーション・サブスクリプションを含む完全なGraphQL APIが自動生成されます。
まとめ
GraphQLサーバーの構築にはApollo Serverが最も広く使われています。DataLoaderでN+1問題を解決し、コンテキストで認証・DBを管理することで堅牢なGraphQL APIが構築できます。Hasuraは既存のPostgreSQLテーブルから即座にGraphQL APIを生成したい場合の強力な選択肢です。