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

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

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

## Relayを選択したい(と今のところ思っている)理由
React Relayのフレームワークに乗っかった方が、生産性が高く、メンテするコード量も少なりそうなので、だったら一度全力で乗っかってみようというフェーズです。  

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

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

https://relay.dev/docs/en/graphql-server-specification

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

https://graphql.org/learn/global-object-identification/

## Lighthouse: LaravelベースのGraphQLサーバー 
Lighthouseは、Laravelに組み込めるGraphQLサーバーです。
https://lighthouse-php.com/
  
Eloquentと統合可能なディレクティブが多数用意されていて、CRUDだけであればコード量も少なく、GraphQLエンドポイントを構築できそうです。
(リレーション定義等は必要なので、全く書かなくて良いわけではありませんでした。)

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

```graphql
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側でディレクティブを解釈した結果は以下となります。（関連部分の抜粋)

```graphql
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テーブルとレコードが格納されているとします。  

```sql
`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固有です。）

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

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

### cursor connection 前提のクエリ
まずは以下のクエリを投げてみます。
（まだカーソルは指定していません。）

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

```

その結果が以下です。

```json

{
  "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移行のデータを要求するクエリを投げてみます。  


```graphql
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番号を指定する方式でのページング機能です。

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

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

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


## Relay対応の楽チンGraphQLサーバをもとめて...
Laravelという資産も生かせて、かつ簡単にGraphQLサーバ建てられそうで良いなあと思っていましたが、Relay前提では厳しそうです。惜しい。
そして今度は [harura](https://hasura.io/) がいいかなと見始めています。