📑

GraphQLのMutationに入門する

2025/02/23に公開

はじめに

前回の記事では、GraphQLの基本的な仕組みやgqlgenを使ったAPIの作成方法を紹介しました。

今回はMutationを利用し、Todoの追加・変更・削除を実装してみます。
本来はデータベースを用意する必要がありますが、GraphQLではメモリ上にデータを保持することで動作を確認できます。


Mutationとは?

GraphQLでは、データの取得はQueryを使いますが、データの追加・更新・削除を行う場合は Mutationを使用します

本記事では、Todoを追加・更新・削除を行う処理を追加します。


Mutationの実装手順

1. スキーマにMutationを追加

graph/schema.graphqlsのtype Mutationの構造体を以下のように変更します。

type Mutation {
  createTodo(input: NewTodo!): Todo!
  updateTodo(id: ID!, text: String, done: Boolean): Todo
  deleteTodo(id: ID!): Boolean!
}

2. モデルを定義

graph/model/models_gen.goに、新規Todoを受け取るためのNewTodo型を追加する必要がありますが、前回の記事の時点で、追加済となっていると思います。

package model

type NewTodo struct {
  Text   string `json:"text"`
  UserID string `json:"userId"`
}

これで、クライアントから受け取るtextuserIdのデータ構造を定義できました。


3. リゾルバを実装

次に、graph/schema.resolvers.gocreateTodo, updateTodo, deleteTodoの処理を実装します。

スキーマを変更したので、graph/schema.resolvers.goを削除し、再度リゾルバを生成してください。

go run github.com/99designs/gqlgen generate

graph/schema.resolvers.goを以下のソースコードに置き換えてください。

package graph

// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.66

import (
	"context"
	"example/graph/model"
	"fmt"
)

// サンプルユーザー
var users = []*model.User{
	{ID: "1", Name: "Alice"},
	{ID: "2", Name: "Bob"},
}

// サンプル Todo
var todos = []*model.Todo{
	{ID: "1", Text: "Alice Todo", Done: false, User: users[0]},
	{ID: "2", Text: "Bob Todo", Done: true, User: users[1]},
}

// `createTodo`のリゾルバを実装
func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
	newTodo := &model.Todo{
		ID:   fmt.Sprintf("%d", len(todos)+1),
		Text: input.Text,
		Done: false,
		User: &model.User{ID: input.UserID, Name: "Unknown"},
	}

	todos = append(todos, newTodo)
	return newTodo, nil
}

// `updateTodo`のリゾルバを実装
func (r *mutationResolver) UpdateTodo(ctx context.Context, id string, text *string, done *bool) (*model.Todo, error) {
	for _, todo := range todos {
		if todo.ID == id {
			if text != nil {
				todo.Text = *text
			}
			if done != nil {
				todo.Done = *done
			}
			return todo, nil
		}
	}
	return nil, fmt.Errorf("todo not found")
}

// `deleteTodo`のリゾルバを実装
func (r *mutationResolver) DeleteTodo(ctx context.Context, id string) (bool, error) {
	for i, todo := range todos {
		if todo.ID == id {
			todos = append(todos[:i], todos[i+1:]...)
			return true, nil
		}
	}
	return false, fmt.Errorf("todo not found")
}

// `todos`のリゾルバ(全てのTodoを取得)
func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
	return todos, nil
}

// Mutation returns MutationResolver implementation.
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }

// Query returns QueryResolver implementation.
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }

type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }



4. サーバーを起動し、Mutationを実行

go run server.go

GraphQL Playgroundで、以下のMutationを実行し、データが変更されるか確認します。

Todo作成

mutation {
  createTodo(input: { text: "Added Alice's Todo", userId: "1" }) {
    id
    text
    done
    user {
      id
      name
    }
  }
}

Todo更新

mutation {
  updateTodo(id: "1", text: "Updated Alice's Todo", done: true) {
    id
    text
    done
  }
}

Todo 削除

mutation {
  deleteTodo(id: "2")
}

結果確認

以下を実行し、追加・更新・削除

query {
  todos {
    id
    text
    done
    user {
      id
      name
    }
  }
}

Discussion