LoginSignup
0
1

More than 3 years have passed since last update.

Laravel Lighthouse は React Relay のcursor connectionに対応できなくて惜しい

Posted at

React Relay

GraphQLクライアントです。
https://relay.dev/

他に代表的なものとしてApollo clientがありますが現状Relayを素振り中です。

Relayを選択したい(と今のところ思っている)理由

React Relayのフレームワークに乗っかった方が、生産性が高く、メンテするコード量も少なりそうなので、だったら一度全力で乗っかってみようというフェーズです。

Apollo clientと比較する点の一つとして、RelayではGraphQLサーバ側の仕様に制約を設けています。

  • 識別子(ID)の定義の仕方
  • コネクション(ページ情報)の定義の仕方 (cursor connection)
  • mutation 引数の定義の仕方
  • などなど...

制約があるというとネガティブな印象もありますが、これらの制約があるおかげで、オブジェクトの再取得を効率化できたり、ページネーション周りで必要なスキーマ定義が整備され、設計・実装時に考えることが(おそらく)減ります。
識別子に関しては、このRelayの制約がそのままベストプラクティスとしてGraphQL公式に書かれたりします。

Lighthouse: LaravelベースのGraphQLサーバー

Lighthouseは、Laravelに組み込めるGraphQLサーバーです。
https://lighthouse-php.com/

Eloquentと統合可能なディレクティブが多数用意されていて、CRUDだけであればコード量も少なく、GraphQLエンドポイントを構築できそうです。
(リレーション定義等は必要なので、全く書かなくて良いわけではありませんでした。)

このディレクティブが非常に楽チンで、@hasManyとか書いておくだけで、Relayのコネクション定義を満たしたスキーマを生成してくれます。
例えばこのようなスキーマを書いたとします。

type User @model {
  id: ID! @globalId
  name: String!
  projects: [Project!] @hasMany(type: "connection", relation: "projects")
}

type Project @model {
  id: ID! @globalId
  name: String!
  status: ProjectStatus!
  createdAt: DateTime! @rename(attribute: created_at)
  updatedAt: DateTime! @rename(attribute: created_at)
}

Lighthouse側でディレクティブを解釈した結果は以下となります。(関連部分の抜粋)

type User implements Node {
  id: ID!
  name: String!
  ownerName: String!
  iconUrl: String
  projects(
    first: Int!
    after: String
  ): ProjectConnection
}

interface Node {
  id: ID!
}

type ProjectConnection {
  pageInfo: PageInfo!
  edges: [ProjectEdge]
}

type ProjectEdge {
  node: Project
  cursor: String!
}

type Project implements Node {
  id: ID!
  name: String!
  status: ProjectStatus!
  createdAt: DateTime!
  updatedAt: DateTime!
}

Relayの制約も既に考慮されていて、Laravelの資産も使えて、効率的にGraphQLエンドポイントが構築できます!

しかし、cursor connection には対応していなかった

これが非常に残念かつ致命的と思われるところで、Lighthouseのドキュメントに以下のような記述がありました。

Lighthouse does not support actual cursor-based pagination as of now, see https://github.com/nuwave/lighthouse/issues/311 for details. Under the hood, the "cursor" is decoded into a page offset.

以下のイシューで議論されているようですが、どうも対応予定がなさそうな感じです。
https://github.com/nuwave/lighthouse/issues/311

connectionの挙動確認

各種スキーマなど

RDBに以下のusersテーブルとレコードが格納されているとします。

`users` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) 

(* 名称は、php fakerでランダム生成したものです。)

id name
1 若松 修平
2 藤本 千代
3 廣川 舞
4 宮沢 浩
5 小泉 涼平

そして、以下のGraphQLスキーマがあるとします。 
(model 等のディレクティブはLighthouse固有です。)

type User @model {
  id: ID! @globalId
  name: String!
}

type Query {
  users: [User!]! @paginate(type: "connection")
}

cursor connection 前提のクエリ

まずは以下のクエリを投げてみます。
(まだカーソルは指定していません。)

query users {
  users(first: 3) {
    pageInfo {
      count
      currentPage
      total
    }
    edges {
      cursor
        node {
        id
        ... on User {
          name    
        }
      }
    }
  }
}

その結果が以下です。


{
  "data": {
    "users": {
      "pageInfo": {
        "count": 3,
        "currentPage": 1,
        "total": 10
      },
      "edges": [
        {
          "cursor": "MQ==",
          "node": {
            "id": "VXNlcjox",
            "name": "若松 修平"
          }
        },
        {
          "cursor": "Mg==",
          "node": {
            "id": "VXNlcjoy",
            "name": "藤本 千代"
          }
        },
        {
          "cursor": "Mw==",
          "node": {
            "id": "VXNlcjoz",
            "name": "廣川 舞"
          }
        }
      ]
    }
  }
}

2番目のユーザ(id="VXNlcjoy")について返却されたcursorを指定して、今度はこのcursor移行のデータを要求するクエリを投げてみます。

query users {
  users(first: 3 after: "Mg==") {
    pageInfo {
      count
      currentPage
      total
    }
    edges {
      cursor
        node {
        id
        ... on User {
          name    
        }
      }
    }
  }
}

この結果は、先ほどのクエリと全く変化がありませんでした。
つまり、afterにcursorを指定しても、それが効いていないことになります。

期待動作としては、1番目のユーザ(id="VXNlcjox")が除外されて、2,3,4番目のユーザが返却される、というものでしたが、確かにcursor connectionに対応されていないようです。

非Relayならpaginatorを使用できる

Relayの場合は cursor connectionが必須ですが、そうでない場合(Apollo Clientなどを使用する場合)は、この制約はありません。
Lighthouseでは、cursor connectionの他に、paginatorもサポートしています。
これは、page番号を指定する方式でのページング機能です。

type Query {
  users: [User!]! @paginate(type: "paginator")
}

以下のようなクエリが書けて、こちらは期待通りに動作しました。

query users {
  users(first: 3 page: 2) {
    paginatorInfo {
      count
      currentPage
      total
      currentPage
    }
    data {
      id
      name
    }
  }
}

Relay対応の楽チンGraphQLサーバをもとめて...

Laravelという資産も生かせて、かつ簡単にGraphQLサーバ建てられそうで良いなあと思っていましたが、Relay前提では厳しそうです。惜しい。
そして今度は harura がいいかなと見始めています。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1