前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在以太坊上构建 GraphQL API

在以太坊上构建 GraphQL API

作者头像
Tiny熊
发布2021-06-10 01:16:47
1.7K0
发布2021-06-10 01:16:47
举报

  • 译文出自:登链翻译计划[1]
  • 译者:翻译小组[2]
  • 校对:Tiny 熊[3]

接上篇以太坊全栈开发完全指南[4]

dapp[5]的数量继续爆炸性增长,对开发人员(使用 Solidity[6]或其他区块链语言的)的需求[7]也越来越大。

作为一名刚进入这个领域[8]的开发人员,我很快就发现,与区块链交互和与传统网络的交互有很大的不同。在以太坊(或者其他区块链)上,数据不可以直接从其他 app 或前端拿来用,你需要重新组织数据并给数据建索引,以便可以有效的检索。

在传统网络上,这是数据库在中心化技术栈中所做的工作,但在Web3 栈[9]中缺少索引层。

在传统的 web 堆栈中,数据库、服务器和 api 在将数据返回到应用程序(通常是通过某种 http 请求)之前,会对数据进行查询、筛选、排序、分页、分组和连接。但直接从以太坊或其他区块链读取数据时,这些类型的数据转换是不可能的。

过去,开发人员通过建立自己的中心化索引服务器来解决这个问题——从区块链中提取数据,存储在数据库中,然后通过 API 公开。这需要大量的工程和硬件资源,并破坏了去中心化的重要的安全特性。

如何在区块链数据之上构建一个可以轻松部署到去中心化网络基础设施的 API?让我们来了解一下。

去中心化网络基础设施

去中心化网络通常被称为Web3[10]。Web3 在我们今天已经熟知的互联网基础上增加了这些特点:

  • 去中心化的
  • 可验证的
  • 抗审查的
  • 自治的

想更多了解 Web3,请视频[11]

为了实现去中心化,协议定义了提供一系列数字服务的网络,如计算、存储、带宽、身份和其他没有中介的网络基础设施。这些协议[12]通常分布在多个节点(服务器)上,使任何希望成为网络一部分并提供服务的人都能参与。

以确保网络本身的安全性和完整性,还需要制定规则激励[13]网络参与者为任何消费它们的人提供最高质量的服务。这通常是通过智能合约中编写的共识机制来实现的,合约实现了各种类型的博弈论和加密经济设计[14]

什么是真正的去中心化的服务?

构建在 The Graph 之上

本文中,我们将研究这样一个协议:The Graph[15],以及如何使用存储在以太坊区块链中的数据来构建和部署自己的 GraphQL API。

Graph 是一个索引协议,用于在像以太坊区块链或IPFS[16]这样的网络上进行查询。任何人都可以建立和发布开放的 API,称为 subgraph —— 让数据访问变得容易。

subgraph 定义了你希望通过 GraphQL API 提供的数据、数据源和数据访问模式。开发者可以选择直接使用别人已经部署[17]的 subgraph,或者自己定义并部署 subgraph。

开发者可以通过将他们的 subgraph 部署到托管服务或网络中来创建开放的 API,根据其 API 的使用量来收费。

subgraph 主要由下面几个部分组成的:

1.GraphQL Schema

GraphQL Schema 定义了你想保存和查询的数据类型/实体。也可定义如关系或全文搜索的配置项。

2.subgraph 清单( yaml 配置)

manifest[18]定义了 subgraph 索引的智能合约、合约的ABI[19]、关注这些合约的事件,以及如何将事件数据映射到 Graph 节点存储并允许查询。

3. AssemblyScript 映射

AssemblyScript 映射允许您使用 schema 中定义的实体类型保存要索引的数据。Graph CLI[20]还使用 schema 与智能合约的 ABI 的组合生成 AssemblyScript 类型。

让我们开始构建

现在我们已经很好地理解了 The Graph 以及它是如何工作的,让我们开始编写一些代码。

在本教程中,我们将建立一个 subgraph,用于从Zora 智能合约[21]中查询 NTF 数据,实现获取 NFT 以及其所有者的查询,并建立它们之间的关系。

安装依赖

要成功完成本教程,你的电脑上应该安装有Node.js[22]。我建议使用nvm[23]或fnm[24]来管理 Node.js 版本。

在 Graph 浏览器中创建项目

首先,请打开Graph Explorer[25],并登录或创建一个新账户。

接下来,进入dashboard[26],点击添加 subgraph ,创建一个新的 subgraph。

像下面配置 subgraph:

  • subgraph 名称 - Zoranfts subgraph
  • 副标题 - 一个用于查询 NFT 的 subgraph
  • 可选 - 填写描述和 GITHUB URL 属性

subgraph 创建后,接下来我们将使用 Graph CLI 在本地初始化该 subgraph 。

使用 Graph CLI 初始化一个新的 subgraph

安装 Graph CLI:

代码语言:javascript
复制
$ npm install -g @graphprotocol/graph-cli

# or

$ yarn global add @graphprotocol/graph-cli

当 Graph CLI 安装好后,你可以用 Graph CLI 的 init命令来初始化 subgraph 。

有两种方法:

  1. 从一个例子 subgraph 初始化
代码语言:javascript
复制
$ graph init --from-example <GITHUB_USERNAME>/<SUBGRAPH_NAME> [<DIRECTORY>]
  1. 从已有的智能合约初始化

如果你已经有一个智能合约部署在以太坊主网或一个测试网,从这个合约初始化一个新的 subgraph 会是一个更简单的启动和运行的方法。

代码语言:javascript
复制
$ graph init --from-contract <CONTRACT_ADDRESS> \
  [--network <ETHEREUM_NETWORK>] \
  [--abi <FILE>] \
  <GITHUB_USER>/<SUBGRAPH_NAME> [<DIRECTORY>]

在本文的例子中,我们将使用Zora Token Contract[27],所以我们可以通过使用--from-contract标志传入合约地址,从该合约地址启动。

代码语言:javascript
复制
$ graph init --from-contract 0xabEFBc9fD2F806065b4f3C237d4b59D9A97Bcac7 --network mainnet  \
--contract-name Token --index-events

? Subgraph name › your-username/Zoranftsubgraph
? Directory to create the subgraph in › Zoranftsubgraph
? Ethereum network › Mainnet
? Contract address › 0xabEFBc9fD2F806065b4f3C237d4b59D9A97Bcac7
? Contract Name · Token

这个命令将根据--from-contract的参数(合约地址)生成一个基础的 subgraph 。通过这个合约地址,CLI 将在你的项目中初始化一些东西(包括获取 abi 并将它们保存在 abis 目录中)。

传入--index-events,CLI 将根据合约发出的事件,在schema.graphqlsrc/mapping.ts中自动为我们填充一些代码。

subgraph 的主要配置和定义在 subgraph.yaml 文件中。subgraph 的代码库由几个文件组成:

  • subgraph.yaml:一个包含 subgraph 清单的 YAML 文件
  • schema.graphql:一个 GraphQL schema,它定义了你的 subgraph 所存储的数据,以及如何通过 GraphQL 查询它。
  • AssemblyScript 映射。AssemblyScript 代码,将以太坊中的事件数据转换为 schema 中定义的实体(例如,本教程中的 mapping.ts)。

我们要处理的 subgraph.yaml 条目有:

  • description(可选):对 subgraph 是什么的可读描述。当 subgraph 被部署到托管服务时,该描述将由 Graph 浏览器显示。
  • repository(可选):可以找到 subgraph 清单的代码库的 URL。Graph 浏览器也会显示这一点。
  • dataSources.source:subgraph 来源的智能合约的地址,以及要使用的智能合约的 ABI。地址是可选的;省略它则会在所有合约搜索匹配事件。
  • dataSources.source.startBlock(可选):数据源开始索引的区块的编号。在大多数情况下,我们建议使用创建合约的区块。
  • dataSources.mapping.entities:数据源写入存储的实体。每个实体的 schema 都在 schema.graphql 文件中定义。
  • dataSources.mapping.abis:一个或多个命名的 ABI 文件,用于源合约以及你在映射中与之交互的任何其他智能合约。
  • dataSources.mapping.eventHandlers:列出该 subgraph 响应的智能合约事件和映射的处理程序--在例子中是 ./src/mapping.ts --将这些事件转化为存储中的实体。

定义实体

通过 The Graph,在 schema.graphql 中定义实体类型,Graph Node 将生成顶层字段,用于查询该实体类型的单个实例和集合。每个成为实体的类型都需要用 @entity 指令来注释。

我们要索引的实体/数据是 TokenUser 。这样,就可以对用户创建的代币以及用户本身进行索引。

要做到这一点,用以下代码更新 schema.graphql

代码语言:javascript
复制
type Token @entity {
  id: ID!
  tokenID: BigInt!
  contentURI: String!
  metadataURI: String!
  creator: User!
  owner: User!
}

type User @entity {
  id: ID!
  tokens: [Token!]! @derivedFrom(field: "owner")
  created: [Token!]! @derivedFrom(field: "creator")
}

通过@derivedFrom建立关系(来自文档)

通过@derivedFrom字段在实体上定义反向查询,这样就在实体上创建了一个虚拟字段,使它可以被查询,但不能通过映射 API 手动设置。实际上,这是从另一个实体上定义的关系中衍生出来的。这样的关系,对存储关系的两者意义不大,如果只存储一方而派生另一方,则索引和查询性能都会更好。

对于一对多的关系,关系应该总是存储在 “一” 边,而 “多” 边应该总是被导出。以这种方式存储关系,而不是在 “多” 边存储一个实体数组,将使索引和查询 subgraph 的性能大大提升。一般来说,应该尽可能地避免存储实体的数组。

现在我们已经为我们的应用程序创建了 GraphQL Schema ,我们可以在本地生成实体,并开始在 CLI 创建的mappings中使用。

代码语言:javascript
复制
graph codegen

为了确保智能合约、事件和实体的工作更简单并且类型安全,Graph CLI 会从 subgraph 的 GraphQL 模式 和 数据源中包含的合约 ABI 的组合中产生 AssemblyScript 类型。

用实体和映射来更新 subgraph

现在我们可以配置 subgraph.yaml 来使用刚刚创建的实体,并配置它们的映射关系。

要做到这一点,首先用 UserToken 实体更新 dataSources.mapping.entities字段。

代码语言:javascript
复制
entities:
  - Token
  - User

接下来,更新dataSources.mapping.eventHandlers,只包括以下两个事件处理程序:

代码语言:javascript
复制
eventHandlers:
  - event: TokenURIUpdated(indexed uint256,address,string)
    handler: handleTokenURIUpdated
  - event: Transfer(indexed address,indexed address,indexed uint256)
    handler: handleTransfer

最后,更新配置,添加 startBlock

代码语言:javascript
复制
source:
  address: "0xabEFBc9fD2F806065b4f3C237d4b59D9A97Bcac7"
  abi: Token
  startBlock: 11565020

AssemblyScript 映射

接下来,打开 src/mappings.ts ,写下我们在 subgraph subgraph eventHandlers 中定义的映射。

用以下代码更新该文件:

代码语言:javascript
复制
import {
  TokenURIUpdated as TokenURIUpdatedEvent,
  Transfer as TransferEvent,
  Token as TokenContract
} from "../generated/Token/Token"

import {
  Token, User
} from '../generated/schema'

export function handleTokenURIUpdated(event: TokenURIUpdatedEvent): void {
  let token = Token.load(event.params._tokenId.toString());
  token.contentURI = event.params._uri;
  token.save();
}

export function handleTransfer(event: TransferEvent): void {
  let token = Token.load(event.params.tokenId.toString());
  if (!token) {
    token = new Token(event.params.tokenId.toString());
    token.creator = event.params.to.toHexString();
    token.tokenID = event.params.tokenId;

    let tokenContract = TokenContract.bind(event.address);
    token.contentURI = tokenContract.tokenURI(event.params.tokenId);
    token.metadataURI = tokenContract.tokenMetadataURI(event.params.tokenId);
  }
  token.owner = event.params.to.toHexString();
  token.save();

  let user = User.load(event.params.to.toHexString());
  if (!user) {
    user = new User(event.params.to.toHexString());
    user.save();
  }
}

这些映射将处理 token 被创建、转移或者更新时的事件。当这些事件发生时,映射将把数据保存到 subgraph 中。

运行构建

接下来,让我们运行构建,以确保一切配置正确,运行build命令:

代码语言:javascript
复制
$ graph build

如果构建成功,应该看到在你的根目录中生成了一个新的 build 文件夹。

部署 subgraph

要部署,我们可以使用 Graph CLI 运行 deploy 命令。要部署,首先需要为你在 Graph Explorer 中创建的 subgraph 复制 Access token

图形浏览器

接下来,运行以下命令:

代码语言:javascript
复制
$ graph auth https://api.thegraph.com/deploy/ <ACCESS_TOKEN>

$ yarn deploy

当 subgraph 部署完毕,你可以看到它显示在你的仪表板上:

图表仪表板

当你点击 subgraph 的时候,就会打开 Graph explorer:

佐拉subgraph

查询数据

现在我们在仪表板中了,可以开始查询数据了。运行下面的查询,获得一个 token 列表和它们的元数据:

代码语言:javascript
复制
{
  tokens {
    id
    tokenID
    contentURI
    metadataURI
  }
}

我们还可以配置订单方向:

代码语言:javascript
复制
{
  tokens(
    orderBy:id,
    orderDirection: desc
  ) {
    id
    tokenID
    contentURI
    metadataURI
  }
}

或者选择向前跳过一定数量的结果,实现一些基本的分页:

代码语言:javascript
复制
{
  tokens(
    skip: 100,
    orderBy:id,
    orderDirection: desc
  ) {
    id
    tokenID
    contentURI
    metadataURI
  }
}

或者查询用户和他们的相关内容:

代码语言:javascript
复制
{
  users {
    id
    tokens {
      id
      contentURI
    }
  }
}

更新 subgraph

如果我们想对 subgraph 做一些改变,然后重新部署,怎么办?这很容易,让我们来学习如何做。

假设我们想给我们的 subgraph 添加一个新功能。除了我们现有的查询功能外,假设我们想增加按 NFT 创建的时间戳排序的功能。

要做到这一点,我们首先需要在 Token实体中添加一个新的 createdAtTimestamp 字段:

代码语言:javascript
复制
type Token @entity {
  id: ID!
  tokenID: BigInt!
  contentURI: String!
  metadataURI: String!
  creator: User!
  owner: User!
  "Add new createdAtTimesamp field"
  createdAtTimestamp: BigInt!
}

现在我们可以重新运行 codegen :

代码语言:javascript
复制
graph codegen

接下来,我们需要更新映射以保存这个新字段:

代码语言:javascript
复制
// update the handleTransfer function to add the createdAtTimestamp to the token object
export function handleTransfer(event: TransferEvent): void {
  let token = Token.load(event.params.tokenId.toString());
  if (!token) {
    token = new Token(event.params.tokenId.toString());
    token.creator = event.params.to.toHexString();
    token.tokenID = event.params.tokenId;
    // Add the createdAtTimestamp to the token object
    token.createdAtTimestamp = event.block.timestamp;

    let tokenContract = TokenContract.bind(event.address);
    token.contentURI = tokenContract.tokenURI(event.params.tokenId);
    token.metadataURI = tokenContract.tokenMetadataURI(event.params.tokenId);
  }
  token.owner = event.params.to.toHexString();
  token.save();

  let user = User.load(event.params.to.toHexString());
  if (!user) {
    user = new User(event.params.to.toHexString());
    user.save();
  }
}

现在我们可以重新部署 subgraph 了:

代码语言:javascript
复制
$ yarn deploy

一旦 subgraph 被重新部署,我们就可以通过时间戳查询来查看最近创建的 NFTS。

代码语言:javascript
复制
{
  tokens(
    orderBy:createdAtTimestamp,
    orderDirection: desc
  ) {
    id
    tokenID
    contentURI
    metadataURI
  }
}

这个项目的代码库位于这里[28]

接下来的步骤

如果你有兴趣了解更多关于 Web3、构建 Dapps 或构建 subgraph 的信息,请查看以下资源。

The Graph on Twitter - @graphprotocol

全栈式以太坊开发完整指南[29]

The Graph Discord[30]

Solidity Docs[31]

以太坊 Developer Documentation[32]

Austin Griffith on Twitter @austingriffith[33] & Scaffold Eth[34]

Crypto Zombies[35]


本翻译由 Cell Network[36] 赞助支持。

来源:https://dev.to/dabit3/building-graphql-apis-on-ethereum-4poa

参考资料

[1]

登链翻译计划: https://github.com/lbc-team/Pioneer

[2]

翻译小组: https://learnblockchain.cn/people/412

[3]

Tiny 熊: https://learnblockchain.cn/people/15

[4]

以太坊全栈开发完全指南: https://learnblockchain.cn/article/2383

[5]

dapp: https://ethereum.org/en/dapps/

[6]

使用Solidity: https://twitter.com/CryptoCobain/status/1371901082113351680?s=20

[7]

需求: https://twitter.com/FurqanR/status/1389393957126246403?s=20

[8]

刚进入这个领域: https://twitter.com/dabit3/status/1379157277660299264

[9]

Web3栈: https://beta.web3index.org/blog/introducing-the-web3-index

[10]

Web3: https://ethereum.org/en/developers/docs/web2-vs-web3/

[11]

视频: https://www.youtube.com/watch?v=KHwVljhq7NQ

[12]

协议: https://www.youtube.com/watch?v=j2rXJLW_93o

[13]

激励: https://www.youtube.com/watch?v=Nurp3Foqf2w

[14]

加密经济设计: https://thegraph.com/blog/modeling-cryptoeconomic-protocols-as-complex-systems-part-1

[15]

The Graph: https://thegraph.com/

[16]

IPFS: https://ipfs.io/

[17]

已经部署: https://thegraph.com/explorer/

[18]

manifest: https://thegraph.com/docs/define-a-subgraph#the-subgraph-manifest

[19]

ABI: https://docs.soliditylang.org/en/v0.8.4/abi-spec.html

[20]

Graph CLI: https://github.com/graphprotocol/graph-cli

[21]

Zora智能合约: https://etherscan.io/address/0xabEFBc9fD2F806065b4f3C237d4b59D9A97Bcac7

[22]

Node.js: https://nodejs.org/en/

[23]

nvm: https://github.com/nvm-sh/nvm

[24]

fnm: https://github.com/Schniz/fnm/blob/master/docs/commands.md

[25]

Graph Explorer: https://thegraph.com/explorer/dashboard

[26]

dashboard: https://thegraph.com/explorer/dashboard

[27]

Zora Token Contract: https://etherscan.io/address/0xabEFBc9fD2F806065b4f3C237d4b59D9A97Bcac7#code

[28]

这里: https://github.com/dabit3/building-a-subgraph-workshop/tree/main/Zoranftgraph

[29]

全栈式以太坊开发完整指南: https://learnblockchain.cn/article/2383

[30]

The Graph Discord: https://thegraph.com/discord

[31]

Solidity Docs: https://docs.soliditylang.org/

[32]

以太坊 Developer Documentation: https://ethereum.org/en/developers/docs/

[33]

@austingriffith: https://twitter.com/austingriffith

[34]

Scaffold Eth: https://github.com/austintgriffith/scaffold-eth

[35]

Crypto Zombies: https://cryptozombies.io/

[36]

Cell Network: https://www.cellnetwork.io/?utm_souce=learnblockchain

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-05-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 深入浅出区块链技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 去中心化网络基础设施
    • 什么是真正的去中心化的服务?
      • 构建在 The Graph 之上
        • 1.GraphQL Schema
        • 2.subgraph 清单( yaml 配置)
        • 3. AssemblyScript 映射
    • 让我们开始构建
      • 安装依赖
        • 在 Graph 浏览器中创建项目
          • 使用 Graph CLI 初始化一个新的 subgraph
            • 定义实体
              • 通过@derivedFrom建立关系(来自文档)
              • 用实体和映射来更新 subgraph
              • AssemblyScript 映射
                • 运行构建
                • 部署 subgraph
                • 查询数据
                • 更新 subgraph
                • 接下来的步骤
                  • 参考资料
                  相关产品与服务
                  Elasticsearch Service
                  腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档