メインコンテンツへスキップ

The Graph: Web3データクエリ問題の解決

Solidity
スマートコントラクト
クエリ
the graph
react
中級
Markus Waas
2020年9月6日
14 分の読書

今回は、The Graphを詳しく見ていきます。これは昨年、dappsを開発するための標準スタックの一部となりました。 まずは、従来のやり方から見ていきましょう...

The Graphを使用しない場合...

それでは、説明のために簡単な例を見ていきましょう。 私たちは皆ゲームが好きなので、ユーザーがベットするシンプルなゲームを想像してみましょう:

さて、私たちのdappで合計ベット数、勝敗の合計ゲーム数を表示し、誰かが再度プレイするたびにそれを更新したいとしましょう。 アプローチは次のようになります:

  1. totalGamesPlayerWonを取得します。
  2. totalGamesPlayerLostを取得します。
  3. BetPlacedイベントをサブスクライブします。

右に示すようにWeb3のイベント (opens in a new tab)をリッスンできますが、かなりの数のケースを処理する必要があります。

この単純な例では、これでもまだある程度は問題ありません。 しかし、今度は現在のプレイヤーの勝敗ベット額のみを表示したいとしましょう。 残念ながら、それらの値を保存して取得する新しいコントラクトをデプロイするしかありません。 そして、はるかに複雑なスマートコントラクトとdappを想像してみてください。事態はすぐに厄介になる可能性があります。

単純にクエリはできない

これが最適ではない理由は、以下の通りです:

  • すでにデプロイ済みのコントラクトでは機能しない。
  • それらの値を保存するための追加のガス代。
  • イーサリアムノードのデータを取得するために、別の呼び出しが必要になる。

これでは不十分

では、より良い解決策を見ていきましょう。

GraphQLのご紹介

まず、GraphQLについてお話ししましょう。これは元々Facebookによって設計・実装されたものです。 従来のREST APIモデルには馴染みがあるかもしれません。 代わりに、欲しいデータを正確に取得するためのクエリを書けると想像してみてください。

GraphQL API 対 REST API

この2つの画像は、GraphQLの本質をよく捉えています。 右側のクエリでは、欲しいデータを正確に定義できます。そのため、1回のリクエストで必要なものだけをすべて取得できます。 GraphQLサーバーは必要なすべてのデータの取得を処理するため、フロントエンドの利用者側にとっては非常に使いやすくなっています。 ご興味があれば、こちらの分かりやすい説明 (opens in a new tab)で、サーバーがクエリをどのように処理するかを正確に知ることができます。

この知識をもとに、いよいよブロックチェーンとThe Graphの世界に飛び込んでみましょう。

The Graphとは?

ブロックチェーンは分散型データベースですが、通常の場合とは対照的に、このデータベースにはクエリ言語がありません。 データを取得するためのソリューションは、手間がかかるか、あるいはまったく不可能です。 The Graphは、ブロックチェーンのデータをインデックス化し、クエリを実行するための分散型プロトコルです。 お察しの通り、クエリ言語としてGraphQLを使用しています。

The Graph

何かを理解するには例を見るのが一番です。そこで、私たちのGameContractの例でThe Graphを使ってみましょう。

サブグラフの作成方法

データをインデックス化する方法の定義は、サブグラフと呼ばれます。 それには3つのコンポーネントが必要です:

  1. マニフェスト (subgraph.yaml)
  2. スキーマ (schema.graphql)
  3. マッピング (mapping.ts)

マニフェスト (subgraph.yaml)

マニフェストは設定ファイルで、以下を定義します:

  • どのスマートコントラクトをインデックス化するか (アドレス、ネットワーク、ABIなど)
  • どのイベントをリッスンするか
  • 関数呼び出しやブロックなど、他にリッスンするもの
  • 呼び出されるマッピング関数 (下記のmapping.tsを参照)

ここでは複数のコントラクトとハンドラを定義できます。 典型的なセットアップでは、Hardhatプロジェクト内に独自のレポジトリを持つサブグラフフォルダを置きます。 そうすれば、ABIを簡単に参照できます。

利便性のために、Mustacheのようなテンプレートツールを使ってもよいでしょう。 次に、subgraph.template.yamlを作成し、最新のデプロイに基づいてアドレスを挿入します。 より高度なセットアップ例については、例えばAaveサブグラフリポジトリ (opens in a new tab)を参照してください。

完全なドキュメントはこちら (opens in a new tab)でご覧いただけます。

スキーマ (schema.graphql)

スキーマはGraphQLのデータ定義です。 これにより、どのエンティティが存在し、その型は何かを定義できます。 The Graphでサポートされている型は次の通りです

  • バイト
  • ID
  • String
  • Boolean
  • Int
  • BigInt
  • BigDecimal

エンティティを型として使用して、リレーションシップを定義することもできます。 この例では、プレイヤーからベットへの1対多のリレーションシップを定義します。 ! は値が空にできないことを意味します。 完全なドキュメントはこちら (opens in a new tab)でご覧いただけます。

マッピング (mapping.ts)

The Graphのマッピングファイルは、受信したイベントをエンティティに変換する関数を定義します。 これはTypescriptのサブセットであるAssemblyScriptで書かれています。 これは、マッピングの実行をより効率的でポータブルにするために、WASM (WebAssembly) にコンパイルできることを意味します。

subgraph.yamlファイルで名付けられた各関数を定義する必要があります。この例では、handleNewBetの1つだけが必要です。 まず、送信者のアドレスをIDとしてPlayerエンティティをロードしようとします。 それが存在しない場合は、新しいエンティティを作成し、初期値を入力します。

次に、新しいBetエンティティを作成します。 このIDは event.transaction.hash.toHex() + "-" + event.logIndex.toString() となり、常に一意の値を保証します。 スマートコントラクトを介して1つのトランザクションで誰かがplaceBet関数を複数回呼び出す可能性があるため、ハッシュのみでは不十分です。

最後に、すべてのデータでPlayerエンティティを更新できます。 配列に直接プッシュすることはできませんが、ここに示すように更新する必要があります。 ベットを参照するためにIDを使用します。 そして、エンティティを保存するためには最後に .save() が必要です。

完全なドキュメントはこちら (opens in a new tab)でご覧いただけます。 マッピングファイルにログ出力を追加することもできます。こちら (opens in a new tab)を参照してください。

フロントエンドでの使用

Apollo Boostのようなものを使用すると、React dapp (またはApollo-Vue) にThe Graphを簡単に統合できます。 特にReactフックとApolloを使用する場合、データの取得はコンポーネントに単一のGraphQLクエリを記述するのと同じくらい簡単です。 典型的な設定は次のようになります。

そして今、例えばこのようなクエリを書くことができます。 これにより、以下が取得されます

  • 現在のユーザーが勝った回数
  • 現在のユーザーが負けた回数
  • 彼の過去のすべてのベットのタイムスタンプのリスト

すべてGraphQLサーバーへの単一のリクエストで完了します。

魔法

しかし、パズルの最後のピース、つまりサーバーが欠けています。 自分で実行するか、ホストされたサービスを使用することができます。

The Graphサーバー

Graph Explorer: ホストされたサービス

最も簡単な方法は、ホストされたサービスを使用することです。 サブグラフをデプロイするには、こちら (opens in a new tab)の指示に従ってください。 多くのプロジェクトでは、実際にエクスプローラー (opens in a new tab)で既存のサブグラフを見つけることができます。

The Graph Explorer

独自のノードの実行

あるいは、独自のノードを実行することもできます。 ドキュメントはこちら (opens in a new tab)。 これを行う理由の1つは、ホストされたサービスでサポートされていないネットワークを使用する場合かもしれません。 現在サポートされているネットワークはこちら (opens in a new tab)で確認できます。

分散型の未来

GraphQLは、新たに着信するイベントのストリームもサポートしています。 これらは、現在オープンベータ版であるSubstreams (opens in a new tab) を介してグラフ上でサポートされています。

2021年 (opens in a new tab)、The Graphは分散型インデックスネットワークへの移行を開始しました。 この分散型インデックスネットワークのアーキテクチャについては、こちら (opens in a new tab)で詳しく読むことができます。

2つの重要な側面は次のとおりです。

  1. ユーザーはクエリに対してインデクサーに支払います。
  2. インデクサーはグラフトークン (GRT) をステークします。

ページの最終更新: 2026年3月3日

このチュートリアルは役に立ちましたか?