12 minutos
Ferramentas do The Graph Client
Este repositório é o lar das ferramentas de lado do consumidor do The Graph (para ambientes tanto de navegador quanto de NodeJS).
Contexto
As ferramentas fornecidas neste repositório têm o propósito de enriquecer e ampliar o DX, e adicionar a camada necessária para dApps para a implementação de aplicações distribuídas.
Programadores que consomem dados da API GraphQL, do The Graph, geralmente precisam de periféricos para facilitar o consumo de dados, e também de ferramentas que permitem o uso simultâneo de vários indexadores.
Funções e Objetivos
Esta biblioteca tem o intuito de simplificar o aspeto de rede do consumo de dados para dApps. As ferramentas fornecidas neste repositório devem ser executadas em tempo de compilação, para torná-las mais rápidas e eficientes durante o tempo de execução.
As ferramentas fornecidas neste repositório podem ser usadas por si próprias, mas também podem ser usadas com qualquer cliente existente da GraphQL!
Status | Característica | Notas |
---|---|---|
✅ | Múltiplos indexadores | baseado em estratégias de busca |
✅ | Estratégias de Busca | timeout , retry , fallback , race , highestValue |
✅ | Validações e otimizações de tempo de compilação | |
✅ | Composição de Lado do Cliente | com planeador de execução (baseado em GraphQL-Mesh) |
✅ | Manuseamento de Subgraph em Várias Chains | Usa subgraphs semelhantes como uma única fonte |
✅ | Execução Bruta (modo autónomo) | sem embrulho de cliente GraphQL |
✅ | Mutações Locais (do lado do cliente) | |
✅ | Rastreamento Automático de Blocos | rastreamento de números de bloco conforme descrito aqui |
✅ | Paginação Automática | faz várias solicitações numa única chamada para buscar mais do que o limite do indexador |
✅ | Integração com @apollo/client | |
✅ | Integração com urql | |
✅ | Apoio a TypeScript | com o gerador de código da GraphQL embutido e TypedDocumentNode |
✅ | Queries @live | Baseado em sondagem |
Você pode encontrar um esboço expandido de arquitetura aqui
Como Começar
Você pode seguir o Episódio 45 do graphql.wtf
para saber mais sobre o Graph Client:
Para começar, instale a CLI do Graph Client no seu projeto:
1yarn add -D @graphprotocol/client-cli2# ou, com NPM:3npm install --save-dev @graphprotocol/client-cli
A CLI está instalada como dependência de programação, visto que estamos a usá-la para produzir artefactos otimizados de tempo de execução, que podem ser carregados diretamente do seu aplicativo!
Depois, crie um arquivo de configuração (chamado .graphclientrc.yml
) e aponte aos seus endpoints GraphQL oferecidos pelo The Graph, por exemplo:
1# .graphclientrc.yml2sources:3 - name: uniswapv24 handler:5 graphql:6 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
Agora, crie um artefacto de tempo de execução executando a CLI do Graph Client:
1graphclient build
Nota: é necessário executar isso com o prefixo yarn
, ou adicioná-lo como um script no seu package.json
.
Isto deve produzir uma função execute
autónoma, pronta para uso, que serve para executar as operações do GraphQL. A saída deve ser semelhante ao seguinte:
1GraphClient: A limpar artefactos existentes2GraphClient: A ler a configuração3🕸️: A gerar o schema unificado4🕸️: A gerar artefactos5🕸️: A gerar arquivo index no TypeScript6🕸️: A escrever index.ts para módulos ESM no disco.7🕸️: Limpeza8🕸️: Pronto! => .graphclient
Agora, o artefacto .graphclient
está gerado para você, e pode ser importado diretamente do seu código. Você pode executar os seus queries assim:
1import { execute } from '../.graphclient'23const myQuery = gql`4 query pairs {5 pair(id: "0x00004ee988665cdda9a1080d5792cecd16dc1220") {6 id7 token0 {8 id9 symbol10 name11 }12 token1 {13 id14 symbol15 name16 }17 }18 }19`2021async function main() {22 const result = await execute(myQuery, {})23 console.log(result)24}2526main()
Usando o JavaScript Normal Em Vez do TypeScript
Normalmente, a CLI do GraphClient gera os artefactos do cliente como arquivos TypeScript, mas dá para configurar a CLI para gerar arquivos JavaScript e JSON junto com arquivos adicionais de definição de TypeScript, usando --fileType js
ou --fileType json
.
A flag js
gera todos os arquivos como arquivos JavaScript com sintaxe ESM, e a flag json
gera artefactos de origem como JSONs, enquanto um arquivo JavaScript de ponto inicial sai com a antiga sintaxe CommonJS, porque apenas a CommonJS suporta JSONs como módulos.
A menos que use especificamente CommonJS(require
), recomendamos o uso da flag js
.
graphclient --fileType js
- Um exemplo de uso de JavaScript em sintaxe CommonJS com arquivos JSON
- Um exemplo de uso de JavaScript em sintaxe ESM
Ferramentas para Programadores do The Graph Client
A CLI do Graph Client vem com um GraphiQL interno, então é possível experimentar com queries em tempo real.
O schema da GraphQL servido nesse ambiente é o eventual schema baseado em todos os subgraphs compostos, e transformações, que você aplicou.
Para iniciar o GraphiQL, execute o seguinte comando:
1graphclient serve-dev
E abra http://localhost:4000/ para usar o GraphiQL. Agora você pode experimentar o seu schema GraphQL do lado do cliente do The Graph, localmente! 🥳
Exemplos
Você também pode se referir ao diretório de exemplos neste repositório, para exemplos mais avançados e alguns de integração:
- Exemplo de TypeScript & React com
execute
bruto e gerador de código da GraphQL embutido - Modo avulso de TS/JS NodeJS
- Composição de GraphQL do lado do cliente
- Integração com Urql e React
- Integração com NextJS e TypeScript
- Integração com Apollo-Client e React
- Integração com React-Query
- Mesclagem entre chains (mesmo Subgraph, chains diferentes)
- Personalize a execução com Transforms (paginação e rastreamento de bloco automáticos)
Exemplos Avançados e Funções
Como Personalizar Chamadas de Rede
A execução da rede pode ser personalizada (por exemplo, para adicionar cabeçalhos de autenticação) usando operationHeaders
:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 operationHeaders:7 Authorization: Bearer MY_TOKEN
Você também pode usar variáveis de tempo de execução se desejar, e especificá-las de forma declarativa:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 operationHeaders:7 Authorization: Bearer {context.config.apiToken}
Então, será possível especificar que quando executar as operações:
1execute(myQuery, myVariables, {2 config: {3 apiToken: 'MY_TOKEN',4 },5})
A documentação completa do handler graphql
está aqui.
Interpolação de Variáveis de Ambiente
Se deseja usar variáveis de ambiente no seu arquivo de configuração do Graph Client, dá para usar a interpolação com o helper env
:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 operationHeaders:7 Authorization: Bearer {env.MY_API_TOKEN} # tempo de execução
Então, certifique-se que o MY_API_TOKEN
está definido quando executar process.env
.
Você também pode especificar variáveis de ambiente a serem preenchidas no tempo de compilação (durante a execução de graphclient build
) com o uso direto do nome env-var:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 operationHeaders:7 Authorization: Bearer ${MY_API_TOKEN} # tempo de compilação
A documentação completa do handler graphql
está aqui.
Estratégias de Busca e Múltiplos Indexadores do The Graph
É comum usar mais de um indexador em dApps, então para alcançar a experiência ideal com The Graph, você pode especificar várias estratégias de fetch
(busca) para torná-las mais suaves e simples.
Todas as estratégias de fetch
podem ser combinadas para otimizar o fluxo de execução.
`retry`
O mecanismo retry
permite que você especifique as tentativas de repetição de um único ponto final/origem da GraphQL.
The retry flow will execute in both conditions: a netword error, or due to a runtime error (indexing issue/inavailability of the indexer).
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 retry: 2 # Especifique aqui, se tiver um indexador instável ou suscetível a erros
`timeout`
O mecanismo timeout
permite que especifique o timeout
de um ponto final da GraphQL.
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 timeout: 5000 # 5 segundos
`fallback`
O mecanismo fallback
permite especificar o uso de mais de um ponto final da GraphQL, para a mesma fonte.
Isso é útil se quiser usar mais de um indexador para o mesmo Subgraph, e usar uma alternativa quando ocorrer um erro ou timeout. Você também pode usar esta estratégia para usar um indexador personalizado, mas permite que ele recorra ao Serviço Hospedado do The Graph.
1sources:2 - name: uniswapv23 handler:4 graphql:5 strategy: fallback6 sources:7 - endpoint: https://bad-uniswap-v2-api.com8 retry: 29 timeout: 500010 - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
`race`
O mecanismo race
permite especificar o uso de mais de um ponto final da GraphQL, para a mesma fonte, e “correr” (executar em simultâneo) em todas as execuções.
Isto é útil se quiser usar mais de um indexador para o mesmo Subgraph, e permitir que ambas as fontes corram pela resposta mais rápida de todos os indexadores especificados.
1sources:2 - name: uniswapv23 handler:4 graphql:5 strategy: race6 sources:7 - endpoint: https://bad-uniswap-v2-api.com8 - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
`highestValue`
Esta estratégia permite enviar solicitações paralelas para diferentes pontos finais, para a mesma fonte, e escolher a mais atualizada.
Isso é útil se quiser escolher a maioria dos dados sincronizados para o mesmo Subgraph sobre diferentes indexadores/fontes.
1sources:2 - name: uniswapv23 handler:4 graphql:5 strategy: highestValue6 strategyConfig:7 selectionSet: |8 {9 _meta {10 block {11 number12 }13 }14 }15 value: '_meta.block.number'16 sources:17 - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2-118 - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2-2
Rastreamento de Blocos
O Graph Client pode rastrear números de bloco e realizar os seguintes queries ao seguir este padrão com o transformador blockTracking
;
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 transforms:7 - blockTracking:8 # Se desativar a validação de schema, a inicialização pode ser mais rápida9 validateSchema: true10 # Ignore os campos que não quer que sejam rastreados11 ignoreFieldNames: [users, prices]12 # Exclua a operação com os seguintes nomes13 ignoreOperationNames: [NotFollowed]
Experimente um exemplo funcional aqui
Paginação Automática
Com a maioria dos subgraphs, o número de registos que podem ser retirados é limitado. Nesse caso, você tem que enviar várias solicitações com paginação.
1query {2 # Erro se o limite for 10003 users(first: 2000) {4 id5 name6 }7}
Então, você tem que enviar as seguintes operações em sequência:
1query {2 # Erro se o limite for 10003 users(first: 1000) {4 id5 name6 }7}
E após a primeira resposta:
1query {2 # Erro se o limite for 10003 users(first: 1000, skip: 1000) {4 id5 name6 }7}
Após a segunda resposta, os resultados devem ser mesclados manualmente. Porém, em vez disso, o Graph Client permite fazer a primeira, e faz essas múltiplas solicitações para você automaticamente, em segundo plano.
Tudo o que você tem que fazer é:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 transforms:7 - autoPagination:8 # Se desativar a validação de schema, a inicialização fica mais rápida9 validateSchema: true
Experimente um exemplo funcional aqui
Composição de Lado do Cliente
O Graph Client possui suporte integrado para Composição GraphQL no lado do cliente (movido a Soldagem de Schema pelas Ferramentas da GraphQL).
Você pode alavancar esse recurso para criar uma única camada do GraphQL a partir de vários Subgraphs, implantados em vários indexadores.
💡 Dica: Você pode compor quaisquer fontes da GraphQL, e não apenas Subgraphs!
A composição trivial pode ser feita ao adicionar mais de uma fonte da GraphQL ao seu arquivo .graphclientrc.yml
. Veja um exemplo:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 - name: compoundv27 handler:8 graphql:9 endpoint: https://api.thegraph.com/subgraphs/name/graphprotocol/compound-v2
Enquanto não houver conflitos entre os schemas compostos, você pode compor e então executar um único query para ambos os Subgraphs:
1query myQuery {2 # este vem do compound-v23 markets(first: 7) {4 borrowRate5 }6 # este vem do uniswap-v27 pair(id: "0x00004ee988665cdda9a1080d5792cecd16dc1220") {8 id9 token0 {10 id11 }12 token1 {13 id14 }15 }16}
Você também pode resolver conflitos, renomear partes do schema, adicionar campos personalizados da GraphQL e modificar toda a fase de execução.
Para casos de uso avançados com composição, por favor consulte os seguintes recursos:
- Exemplo Avançado de Composição
- Transformações de Schema do Mesh da GraphQL
- Documentação de Soldagem de Schema pelas Ferramentas da GraphQL
Apoio a TypeScript
Se o seu projeto for escrito no TypeScript, você poderá aproveitar o poder de TypedDocumentNode
e ter uma experiência de cliente GraphQL com tipos totalmente definidos.
O modo autónomo da GraphQL, assim como bibliotecas populares de clientes da GraphQL como Apollo-Client e urql, tem suporte integrado ao TypedDocumentNode
!
A CLI do Graph Client vem com uma configuração pronta para o Gerador de Código da GraphQL, e pode gerar TypedDocumentNode
com base nas suas operações da GraphQL.
Para começar, defina as suas operações GraphQL no código do seu aplicativo e aponte para esses arquivos usando a seção documents
do .graphclientrc.yml
:
1sources:2 - # ... suas fontes de GQL/Subgraphs aqui34documents:5 - ./src/example-query.graphql
Você também pode usar expressões Glob, ou até mesmo apontar para arquivos de código, e a CLI vai encontrar os seus queries da GraphQL automaticamente:
1documents:2 - './src/**/*.graphql'3 - './src/**/*.{ts,tsx,js,jsx}'
Agora, execute o comando build
na CLI da GraphQL novamente; a CLI gerará um objeto TypedDocumentNode
sob .graphclient
para cada operação encontrada.
Nomeie as suas operações da GraphQL; caso contrário, a ação será ignorada!
Por exemplo, uma query chamada query ExampleQuery
terá o ExampleQueryDocument
correspondente gerado no .graphclient
. Agora dá para importá-lo e usá-lo para as suas chamadas do GraphQL, e você terá uma experiência com tipos definidos sem escrever ou especificar nenhum TypeScript manualmente:
1import { ExampleQueryDocument, execute } from '../.graphclient'23async function main() {4 // o valor "result" tem tipo definido, e representa a estrutura exata dos campos selecionados no seu query.5 const result = await execute(ExampleQueryDocument, {})6 console.log(result)7}
Mutações de Lado do Cliente
Devido à natureza da configuração do Graph Client, é possível adicionar schemas do lado do cliente, para poder fazer depois um bridge para executar qualquer código arbitrário.
Isto é útil, já que você pode implementar códigos personalizados como parte do seu schema GraphQL e tê-los como um schema unificado de aplicativo que é mais fácil de rastrear e se desenvolver.
Este documento explica como adicionar mutações personalizadas, mas na verdade, você pode adicionar qualquer operação da GraphQL (query/mutação/assinaturas). Veja Como estender o artigo do schema unificado para mais informações sobre este recurso.
Para começar, defina uma seção additionalTypeDefs
no seu arquivo de configuração:
1additionalTypeDefs: |2 # Definir o tipo `Mutation` que falta3 extend schema {4 mutation: Mutation5 }67 type Mutation {8 doSomething(input: SomeCustomInput!): Boolean!9 }1011 input SomeCustomInput {12 field: String!13 }
Em seguida, adicione um ponteiro para um arquivo resolver
personalizado da GraphQL:
1additionalResolvers:2 - './resolvers'
Agora, crie resolver.js
(ou resolvers.ts
) no seu projeto e implemente a sua mutação personalizada:
1module.exports = {2 Mutation: {3 async doSomething(root, args, context, info) {4 // Aqui, você pode executar o que quiser.5 // Por exemplo, usar a biblio `web3`, conectar uma carteira, e por aí vai.67 return true8 },9 },10}
Se usar o TypeScript, você também pode obter uma assinatura totalmente segura com:
1import { Resolvers } from './.graphclient'23// Totalmente definido!4const resolvers: Resolvers = {5 Mutation: {6 async doSomething(root, args, context, info) {7 // Aqui, você pode executar o que quiser.8 // Por exemplo, usar a biblio `web3`, conectar uma carteira, e por aí vai.910 return true11 },12 },13}
Se precisar injetar variáveis de tempo de execução no seu context
de execução da GraphQL, você pode usar o seguinte trecho:
1execute(2 MY_QUERY,3 {},4 {5 myHelper: {}, // isto estará disponível no seu resolver de Mutação como `context.myHelper`6 },7)
Licença
Lançado sob a licença da MIT.