<CodeLearn/>
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スキーマを定義するための宣言的な言語
  • スカラー型(StringIntFloatBooleanID)が基本データ型
  • オブジェクト型でリレーションを定義し、グラフ構造を表現
  • Enum型で固定の選択肢、Input型でMutationの複雑な引数を定義
  • Query(読み取り)、Mutation(変更)、Subscription(リアルタイム)の3つのルート操作型
  • ! でNon-nullable(null不可)を指定し、型安全性を高める