📑
GraphQLのMutationに入門する
はじめに
前回の記事では、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"`
}
これで、クライアントから受け取るtext
とuserId
のデータ構造を定義できました。
3. リゾルバを実装
次に、graph/schema.resolvers.go
にcreateTodo
, 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