patorashのブログ

方向性はまだない

mutationでバルク処理をする場合のアプローチ

mutationでデータを更新するGraphQL APIを作りました。しかしそのAPIが時々しか呼ばれないならいいのですが、頻繁に何度も呼ばれるケースだとAPIのへのアクセスが複数回になり無駄が多いので、バルクアップデートみたいなことはできないか?と指摘を受けました。個人的には、「うーん、そうはいってもAPIってそういうもんでしょ…。」と思っていたのですが、他のサービスのケースとかを参考にしてみますと回答して、調査開始。

1度のmutationで同じAPIを使い回す

ググると、バルク処理用のAPIを作れみたいな記事が見つかってコレジャナイ感があったのですが、遂に以下の記事を見つけました。

blog.grandstack.io

path名をつけることで、同じmutationを複数回使うという方法です。 以下のように使います。

mutation {
  path1: updateUserName(input: { userId: 1, name: "foo" }) {
    user {
      id
      name
    }
  },
  path2: updateUserName(input: { userId: 2, name: "bar" }) {
    user {
      id
      name
    }
  },
  path3: updateUserName(input: { userId: 3, name: "baz" }) {
    user {
      id
      name
    }
  }
}

すると結果は以下のように返ってきます。

{
  "data": {
    "path1": {
      "user": {
        "id": "1",
        "name": "foo"
      }
    },
    "path2": {
      "user": {
        "id": "2",
        "name": "bar"
      }
    },
    "path3": {
      "user": {
        "id": "3",
        "name": "baz"
      }
    }
  }
}

おおお、1回のアクセスで3つ更新できた🎉

バルク処理のエラー検知

でもこれってエラーになったらどう検知するのよ?と思い、無理やりエラーが起きるようにしてみました。上記でいえば、存在しないuser_idを渡すとかです。

mutation {
  path1: updateUserName(input: { userId: 1, name: "foo" }) {
    user {
      id
      name
    }
  },
  path2: updateUserName(input: { userId: 2, name: "bar" }) {
    user {
      id
      name
    }
  },
  path3: updateUserName(input: { userId: -1, name: "baz" }) { # そんなユーザはいない!
    user {
      id
      name
    }
  }
}

そうすると、以下のような感じで返ってきました(実装次第ではありますが…)。

{
  "data": {
    "path1": {
      "user": {
        "id": "1",
        "name": "foo"
      }
    },
    "path2": {
      "user": {
        "id": "2",
        "name": "bar"
      }
    },
    "path3": null
  },
  "errors": [
    {
      "message": "ユーザが見つかりません",
      "locations": [
        # 略
      ],
      "path": [
        "path3"
      ]
    }
  ]
}

errorsの配列の中のpathの値を見たら、どのpathでエラーが起きているかが分かるので、対処しやすくなるかと思います。

所感

1つのデータ更新用のAPIと、バルク更新用のAPIをわざわざ作らないといけないのかなー?🤔と考えていたところだったのですが、これならば実装1つでいけるので、大変やりやすいです🥳ただし、トランザクションが必要になるケースの場合は、バルク更新用のAPIを作らないといけないでしょう。