GraphQLのNodeのうまみをコードを書いて理解する

モチベーション

GraphQLにNodeという概念があるがよく分かっていないので理解したい。NodeはGraphQLの文脈で見かけ、また、一般に公開されているGitHub APIでも登場する概念*1だが、Nodeを知らなくてもGraphQL APIを構築できることを経験しているので謎が深まっている。おそらくGraphQL APIデファクトスタンダードな概念と見ている。コードを書いて理解する。

Relay

"GraphQL Node" 等のフレーズで調べると "Relay" という単語が出てくる。RelayはFacebook(Meta)製のGraphQLクライアント。Relayはオブジェクトの再取得処理とConnectionのページングに強みがあるみたい。

Node

relay.dev

Nodeはグラフ理論では接点という意味で使われるが、GraphQLでのNodeはインターフェースとしてidという単一のフィールドを持つ。オブジェクトの型に対しNodeを準拠させることで以下の処理が楽になるらしい。

  • データの再取得
  • ページネーション

ここまで書いているうちにコードのイメージが付いたので書く。今回はデータの再取得に絞ってコードを書いて旨味を理解する。

つくったもの

Nodeありなしでコードを書いた。schema.graphqlsのリンクを置いておく。

Nodeあり

github.com

Nodeなし

github.com

所感

サーバー側

interface Node {
  id: ID!
}

type Workout implements Node {
  id: ID!
  distance: Float!
}

type User implements Node{
  id: ID!
  name: String!
  workouts: [Workout!]!
}

type Query {
  workouts: [Workout!]
  node(id: ID!): Node
}

...

クライアント側で引きたいオブジェクトに対してNodeを準拠させることでルートのクエリ型で定義するフィールドを集約することができる。一方で、あるNodeに準拠させたオブジェクトを配列で複数受け取りたい場合は個別でフィールドを書く必要がありそう。(上記の workouts: [Workout!])配列が持つNodeの具体的な型を固定できれば引けそう、どうなんだろう。そこまでする必要はなさそう?

型が増えるごとにNodeを導入する旨味は増えそう。あるオブジェクトを再取得する時にオブジェクトがNodeに準拠していることでIDを知ってさえいれば直接取れる。Nodeを定義していない場合は個々にクエリのフィールドとして用意してあげるか、お目当てのオブジェクトにたどり着くまでに複数のネストがあるクエリを実行する、みたいな感じになりそう。

クライアント側

# Nodeあり (インラインフラグメント)
query getWorkout {
  node(id: "{ID}") {
    ... on Workout {
      id
      distance
    }
  }
}

# Nodeあり (フラグメント定義)
query getWorkout {
  node(id: "{ID}") {
    ...WorkoutFragment
  }
}

fragment WorkoutFragment on Workout {
  id
  distance
}

# Nodeなし 
query getWorkout {
  workout(id: "{ID}"){
    id
    distance
  }
}

クエリするときはNodeありになるとフラグメントを使う必要が出てくるが先程の抽象化による旨味を考えると十分許容できる。

参考