SymbolブロックチェーンでGraphQLを使いたい

最近は本業の方でGraphQLを利用することが増えてきました。
そこで、SymbolでもREST APIだけではなくGraphQLを使ってノードからデータを取得できないかちょっと試してみます。

## GraphQLを活用するメリット

### **複数のAPI呼び出しを1つのリクエストで実現**

RESTでは複数のエンドポイントを順番に呼び出す必要がありますが、GraphQLでは必要なデータを一度に取得可能です。
例えば、こんな感じ。

```graphql
query SymbolGqlExample {
  # ネットワークの情報
  getNetworkType {
    description
    name
  }
  # 接続先ノードの状態
  getNodeHealth {
    status {
      apiNode
      db
    }
  }
  # 特定のブロック高の中身
  getBlockByHeight(height: "65536") {
    ... on BlockInfoDTO {
      id
      block {
        beneficiaryAddress
        difficulty
      }
    }
  }
}
```

上記のクエリを実行すると以下のようなレスポンスが取得できます

```json
{
  "data": {
    "getNetworkType": {
      "description": "Symbol Mainnet",
      "name": "mainnet"
    },
    "getNodeHealth": {
      "status": {
        "apiNode": "up",
        "db": "up"
      }
    },
    "getBlockByHeight": {
      "id": "65C57A484CF5710B720DB1A7",
      "block": {
        "beneficiaryAddress": "685B9B33391EFA3C823B696974702CBC56E1A57FE5F1321B",
        "difficulty": "78040468282391"
      }
    }
  }
}
```

### **クライアント側の柔軟性向上**

SQLのSELECTのように、必要なデータだけを取得できます。

```graphql
query SymbolGqlExample {
  # 接続先ノードの状態
  getNodeHealth {
    status {
      # apiNodeの状態はべつにいらねーや
      # apiNode
      db
    }
  }
}
```

結果↓

```jsonc
{
  "data": {
    "getNodeHealth": {
      "status": {
        "db": "up" // dbのステータスだけ取れる
      }
    },
}
```

こんな感じで、必要なデータだけ取得する事もでできるのでネットワークの帯域も節約できます。

## GraphQLを使うために必要なもの

さて、GraphQLを使用するには、**リゾルバ（resolver）**の実装が必要です。リゾルバは、クライアントからのクエリに応じてデータを取得し、返却する役割を果たします。

また、リゾルバを構築するには**スキーマ**が必要です。スキーマはGraphQLでデータの構造やリクエストの形式を定義するものです。

### 今回どうするか
さすがにリゾルバやらスキーマやらを1から作っていくのは大変です。

今回は[symbol-blockchain-community/symbol-rest-client](https://github.com/symbol-blockchain-community/symbol-rest-client/tree/main/schema)で有志の方々がメンテしている**OpenAPI仕様準拠のYAMLファイル**を使って自動生成してみます。（このやり方には課題があるのですがそれは後ほど。）

## やり方

(mod 2024.12.15)
執筆時動かなかったものがようやく動いたのでgithubで公開しました
[symbol-graphql-server](https://github.com/oe-trader/symbol-graphql-server)

こちらのREADME.MD記載の手順で `4.playgroundの起動` まで進めてください。
([日本語版READMEはこちら](https://github.com/oe-trader/symbol-graphql-server/blob/main/README.ja.md))

## 実際に使ってみた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525917/aca50817-a1ae-e6c6-4cd7-45bdcfb6f5b9.png)

Playgroundにアクセスするとこんな感じでGraphQLを試すことができます。

簡単にPlaygroundの使い方を解説すると以下のようになります。

- 左:スキーマのdocumentやクエリの実行履歴などが確認できます。Explorerからはそのスキーマに定義されたフィールドを確認できますので、クエリ作成の参考になります。（どんなデータが取れるのかなど）
- 中央: クエリを書く欄です。フォーマットもできますがコメントが消えるのでしないほうがいいｗ
- 右側: 実行した結果が表示されます。

## まとめと課題

今回は、OpenAPI形式のYAMLを使用してスキーマとリゾルバを自動生成し、REST APIをラップする形でGraphQLを実現しました。

ただし、この手法は以下の課題があります：

- 現状、REST APIをラップしているだけであり、SymbolノードのDBに直接アクセスしているわけではない
- そのため、複数のスキーマからデータを取得しようとした場合、それぞれのスキーマに対応する回数分のREST APIリクエストが内部で実行されてしまう。

将来的には、REST APIに依存せず、ノードのデータベースと直接やり取りできるようなリゾルバの実装を目指します。

この記事で紹介したOpenAPIを用いたGraphQLサーバーは、[Symbol-blockchain-community](https://github.com/symbol-blockchain-community/symbol-graphql-server)のGitHubリポジトリにサンプルとして公開予定です。
※現在は前述した私個人のGithubリポジトリを参照してください。いずれこちらに移します。

本バージョンを`v1`とし、データベース直接接続型の`v2`を順次実装していければなーと思います。

そんなわけで、**[symbol-graphql-server](https://github.com/symbol-blockchain-community/symbol-graphql-server)への貢献者を募集しています！**