「GraphQLのmutationは動的にネストしたリソースを更新するもの」ではない

「参照系はGraphQLだけど、更新系はRESTでPOSTにします」みたいな意見を稀によく見る。 もちろん何かしらのトレードオフを考えてRESTを選択しているのだとは思うのだけど、GraphQLのmutation(要は更新系)を誤解している人も中にはいるのではなかろうか。

先日GraphQLのmutationは難しそう……という意見をもらって、詳しく聞いてみるとmutationについて誤解があるようだった。 もしかしたら同じような勘違いをされている方は他にもいるかもしれないなーと思ったのでまとめておく。

※実際、自分も何となくリソースの更新も柔軟にできるようなイメージを持っていたところはあった。

GraphQLとは

もはや色々な入門記事が溢れているので、詳しく説明することはしないが、 A query language for your API と公式にあるように、以下のようなフォーマットでサーバーサイドとやりとりをすることで、柔軟にデータを取得することができるようになっている。

GraphQLでは参照系をqueryといいます。

query {
  hero {
    name
    # Queries can have comments!
    friends {
      name
    }
  }
}
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

GraphQLというとこの説明が多く、柔軟にリソースを取得できるというところがメリットとして語られる。そのせいか、なんとなく更新系も柔軟にいけるのでは?みたいなイメージを持ってしまうのかもしれない。

GraphQLのmutationとは?

RESTでGETでデータ更新することが基本的にはアンチパターンなように、queryでデータ更新を行うのは推奨されておらず、mutationというものを使う。queryの印象に引っ張られて、mutationも以下のようなフォーマットでネストされたデータ構造を一気に更新できる……みたいなイメージを何となく持ってしまいそうだが、mutationにはそんな機能は用意されていない。

# GraphQLにこんなシンタックスはありません!!!
mutation {
  hero {
    name = "hoge"
    # Queries can have comments!
    friends {
      name = piyopiyo
    }
  }
}

以下のようにRPC形式*1のフォーマットでmutationのリクエストは定義する。

mutation {
  updateHeroName(name: '太郎') {
    # 以下はあくまで戻り値として何を取得したいか
    hero {
       name
    }
  }
}

上記の例でいうとupdateHeroName の中で具体的に何をするのかは自分たちの実装に完全に委ねられている。要はどのデータを実際に更新するかは自分たちの実装次第であり、GraphQLが絡むのはリクエストとレスポンスの型だけとなる。これを見ると、タイトルにあるように 「GraphQLのmutationは動的にネストしたリソースを更新するもの」ではない ということが分かると思う。

戻り値となるJSONのレスポンスに必要なデータはqueryと同じく柔軟に決められるが、データ更新という側面に絞って言えばRESTと比べたmutationの明確なメリット・違いは、リクエストとレスポンスに型が付けられる(付けやすい)ことくらいかもしれない。

GraphQLでmutationを使う意味はないのか?

こうやってみると、GraphQLのmutationはqueryほど強力な機能をもっているわけではなく、あまり使うメリットが感じられないかもしれません。しかしながらGraphQLを使っていればGraphiQLといったツールの恩恵を受けられますし、参照系をGraphQLで作るならあえて更新系をRESTにする必要はないんじゃないかなーというのがちょっとだけGraphQLでAPIを作った自分の感想です。

f:id:suzan2go:20181120234450p:plain
GraphiQLの画面

*1:この言い方が正しいのかわからないけど