GraphQL レッスン2
スキーマ定義
SDL(Schema Definition Language)で型を設計しよう
SDL(Schema Definition Language)とは?
SDLは、GraphQLのスキーマを定義するための言語です。 APIで扱うデータの型、フィールド、リレーションをすべてSDLで記述します。 スキーマはクライアントとサーバー間の「契約」として機能します。
# スキーマファイル(schema.graphql)の基本構造
# オブジェクト型の定義
type User {
id: ID!
name: String!
email: String!
}
# クエリのエントリポイント
type Query {
users: [User!]!
user(id: ID!): User
}
# ミューテーションのエントリポイント
type Mutation {
createUser(name: String!, email: String!): User!
}スキーマを先に設計することで、フロントエンドとバックエンドが並行して開発を進められます。
スカラー型(Scalar Types)
GraphQLに組み込みの基本データ型です。すべてのフィールドは最終的にスカラー型に解決されます。
# 組み込みスカラー型
type Example {
id: ID! # 一意な識別子(文字列として扱われる)
name: String! # UTF-8 文字列
age: Int! # 32ビット整数
score: Float! # 倍精度浮動小数点数
isActive: Boolean! # true / false
}
# カスタムスカラー型の定義
scalar DateTime
scalar JSON
scalar Upload
# カスタムスカラー型を使う
type Post {
id: ID!
title: String!
createdAt: DateTime! # カスタムスカラー型
metadata: JSON # JSONデータ
}カスタムスカラー型を使うには、リゾルバ側でシリアライズ・パースのロジックを実装する必要があります。
オブジェクト型とリレーション
オブジェクト型はフィールドの集合で、型同士をリレーションで結びつけることができます。
# ユーザー型
type User {
id: ID!
name: String!
email: String!
profile: Profile # 1対1 リレーション
posts: [Post!]! # 1対多 リレーション
friends: [User!]! # 自己参照リレーション
}
# プロフィール型
type Profile {
bio: String
avatar: String
website: String
user: User! # 逆方向のリレーション
}
# 投稿型
type Post {
id: ID!
title: String!
body: String!
author: User! # 多対1 リレーション
tags: [Tag!]! # 多対多 リレーション
comments: [Comment!]!
}
# タグ型
type Tag {
id: ID!
name: String!
posts: [Post!]! # 多対多の逆方向
}
# コメント型
type Comment {
id: ID!
text: String!
author: User!
post: Post!
}GraphQLのリレーションはRDBの外部キーとは異なり、グラフ構造として自由にナビゲートできます。
Enum型とInput型
Enum型は固定の選択肢を定義し、Input型はMutationの引数として使う複雑な入力を定義します。
# Enum型:固定の選択肢
enum Role {
ADMIN
EDITOR
VIEWER
}
enum PostStatus {
DRAFT
PUBLISHED
ARCHIVED
}
# Enum型をフィールドで使う
type User {
id: ID!
name: String!
role: Role! # ADMIN | EDITOR | VIEWER
}
type Post {
id: ID!
title: String!
status: PostStatus! # DRAFT | PUBLISHED | ARCHIVED
}
# Input型:Mutationの引数をまとめる
input CreateUserInput {
name: String!
email: String!
role: Role = VIEWER # デフォルト値
}
input UpdatePostInput {
title: String
body: String
status: PostStatus
}
# Input型をMutationで使う
type Mutation {
createUser(input: CreateUserInput!): User!
updatePost(id: ID!, input: UpdatePostInput!): Post
}typeは出力(レスポンス)に、inputは入力(引数)に使います。混同しないようにしましょう。
Query / Mutation / Subscription
GraphQLには3つのルート操作型があります。これらがAPIのエントリポイントになります。
# Query:データの読み取り(GET相当)
type Query {
users: [User!]!
user(id: ID!): User
posts(status: PostStatus): [Post!]!
searchPosts(keyword: String!): [Post!]!
}
# Mutation:データの作成・更新・削除(POST/PUT/DELETE相当)
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User
deleteUser(id: ID!): Boolean!
createPost(input: CreatePostInput!): Post!
publishPost(id: ID!): Post!
}
# Subscription:リアルタイム通知(WebSocket)
type Subscription {
postCreated: Post!
userStatusChanged(userId: ID!): User!
commentAdded(postId: ID!): Comment!
}Query
データの取得。並列実行される
Mutation
データの変更。順次実行される
Subscription
リアルタイムイベント。WebSocket使用
Nullable と Non-nullable
GraphQLでは、すべてのフィールドはデフォルトでnullable(nullを返す可能性がある)です。! を付けるとNon-nullable(必ず値を返す)になります。
type User {
# Non-nullable: 必ず値を返す(nullは不可)
id: ID!
name: String!
# Nullable: nullを返す可能性がある
bio: String # null または "文字列"
age: Int # null または 数値
# 配列の場合、!の位置に注意
posts: [Post!]! # 配列は必須、要素もnull不可
# → 空配列 [] はOK
# → [post1, post2] はOK
# → null はNG
# → [null] はNG
tags: [String]! # 配列は必須、要素はnull可
# → [] はOK
# → ["tag1", null] はOK
# → null はNG
friends: [User!] # 配列自体はnull可、要素はnull不可
# → null はOK
# → [] はOK
# → [user1] はOK
# → [null] はNG
nicknames: [String] # 配列もnull可、要素もnull可
# → なんでもOK
}Non-nullable を適切に使うことで、クライアント側でnullチェックを減らし、型安全なコードが書けます。
まとめ
- SDLはGraphQLスキーマを定義するための宣言的な言語
- スカラー型(
String、Int、Float、Boolean、ID)が基本データ型 - オブジェクト型でリレーションを定義し、グラフ構造を表現
- Enum型で固定の選択肢、Input型でMutationの複雑な引数を定義
- Query(読み取り)、Mutation(変更)、Subscription(リアルタイム)の3つのルート操作型
!でNon-nullable(null不可)を指定し、型安全性を高める