10 minutos
API GraphQL
Aprenda sobre a API de Queries da GraphQL, usada no The Graph.
O Que é a GraphQL?
A GraphQL é uma linguagem de queries para APIs e um sistema de tempo de execução (runtime) para executar esses queries, com os seus dados já existentes. O The Graph usa a GraphQL para fazer queries em subgraphs.
Para entender melhor o papel maior da GraphQL, veja como programar e criar um subgraph.
Queries com a GraphQL
No seu schema de subgraph, você definirá tipos chamados Entities
(“Entidades”). Para cada tipo Entity
, campos entity
e entities
serão gerados no tipo de nível superior Query
.
Observação: query
não precisa ser incluído no topo do query graphql
enquanto usar o The Graph.
Exemplos
Query para uma entidade Token
definida no seu schema:
1{2 token(id: "1") {3 id4 owner5 }6}
Nota: Ao fazer um query para uma única entidade, é obrigatório um campo id
; este deve ser escrito como string.
Faça um query de todas as entidades Token
:
1{2 tokens {3 id4 owner5 }6}
Organização
Quando fizer um query de uma coleção, você poderá:
- Use o parâmetro
orderBy
para organizar os resultados por atributos específicos. - Use
orderDirection
para especificar a direção da organização;asc
para ordem crescente oudesc
para decrescente.
Exemplo
1{2 tokens(orderBy: price, orderDirection: asc) {3 id4 owner5 }6}
Exemplo para organização de entidade aninhada
Com o lançamento da versão v0.30.0
do Graph Node, as entidades podem ser organizadas com base nas entidades aninhadas.
No seguinte exemplo, organizamos os tokens pelo nome do proprietário:
1{2 tokens(orderBy: owner__name, orderDirection: asc) {3 id4 owner {5 name6 }7 }8}
Atualmente, pode-se organizar por tipos String
or ID
de nível único em campos @entity
e @derivedFrom
. Infelizmente, ainda não há apoio para organização por interfaces em entidades de nível único, que é a organização por campos que são arranjos e entidades aninhadas.
Paginação
Quando fizer um query de uma coleção, recomendamos:
- Usar o parâmetro
first
para paginar a partir do início da coleção.- A ordem padrão de organização é por
ID
em ordem alfanumérica crescente, não por data e hora de criação.
- A ordem padrão de organização é por
- Usar o parâmetro
skip
para pular entidades e paginar. Por exemplo:first:100
mostra as primeiras 100 entidades efirst:100, skip:100
mostra as próximas 100. - Evitar usar valores
skip
em queries, já que o desempenho destes normalmente não é bom. Para resgatar um grande volume de itens, recomendamos paginar entidades com base num atributo, conforme demonstrado no exemplo acima.
Exemplo com first
Consulte os 10 primeiros tokens:
1{2 tokens(first: 10) {3 id4 owner5 }6}
Para fazer queries sobre grupos de entidades no meio de uma coleção, o parâmetro skip
pode ser usado em conjunto com o parâmetro first
para pular um número especificado de entidades, a começar no início da coleção.
Exemplo com first
e skip
Faça query de 10 entidades Token, deslocadas por 10 posições do começo da coleção:
1{2 tokens(first: 10, skip: 10) {3 id4 owner5 }6}
Exemplo com first
e id_ge
Se um cliente precisar retirar um grande volume de entidades, é muito mais eficiente basear queries num atributo e filtrar pelo mesmo. Por exemplo, um cliente retiraria um número grande de tokens com este query:
1query manyTokens($lastID: String) {2 tokens(first: 1000, where: { id_gt: $lastID }) {3 id4 owner5 }6}
Na primeira vez, ele enviaria o query com lastID = ""
, e nas solicitações seguintes, configuraria lastID
no atributo id
da última entidade no pedido anterior. Este método será mais eficiente do que usar valores skip
crescentes.
Filtragem
- O parâmetro
where
pode ser usado nos seus queries para filtrar propriedades diferentes. - Vários valores podem ser filtrados dentro do parâmetro
where
.
Exemplo com where
Faça um query sobre desafios com o resultado failed
(falha):
1{2 challenges(where: { outcome: "failed" }) {3 challenger4 outcome5 application {6 id7 }8 }9}
É possível usar sufixos como _gt
, ou _lte
, para comparar valores:
Exemplo para filtragem de alcance
1{2 applications(where: { deposit_gt: "10000000000" }) {3 id4 whitelisted5 deposit6 }7}
Exemplo para filtragem de blocos
Também dá para filtrar entidades atualizadas dentro de, ou depois de, um bloco específico com _change_block(number_gte: Int)
.
Isto pode servir caso o seu plano seja retirar apenas entidades que mudaram, por exemplo, desde a última vez que você pesquisou. Também pode ser bom investigar ou debugar como as entidades mudam no seu subgraph (se combinado com um filtro de blocos, pode isolar apenas entidades que mudaram em um bloco específico).
1{2 applications(where: { _change_block: { number_gte: 100 } }) {3 id4 whitelisted5 deposit6 }7}
Exemplo para filtragem de entidade ninhada
É possível usar filtros com base em entidades aninhadas nos campos com o sufixo _
.
Isto é bom caso mire retirar apenas entidades cujas entidades de nível-filho correspondem às condições requeridas.
1{2 challenges(where: { application_: { id: 1 } }) {3 challenger4 outcome5 application {6 id7 }8 }9}
Operadores lógicos
Com o lançamento da versão v0.30.0
do Graph Node, é possível agrupar vários parâmetros no mesmo argumento where
, com os operadores and
ou or
, para filtrar resultados com base em mais de um critério.
Operador AND
O exemplo a seguir filtra por desafios com o outcome
(“resultado”) succeeded
(“sucesso”), e number
(“número”) maior que ou igual a 100
.
1{2 challenges(where: { and: [{ number_gte: 100 }, { outcome: "succeeded" }] }) {3 challenger4 outcome5 application {6 id7 }8 }9}
Açúcar sintático: O query acima pode ser simplificado ao retirar o operador and
, com o passe de uma sub-expressão separada por vírgulas.
1{2 challenges(where: { number_gte: 100, outcome: "succeeded" }) {3 challenger4 outcome5 application {6 id7 }8 }9}
Operador OR
O exemplo a seguir filtra desafios com o outcome
succeeded
, e o number
maior que ou igual a 100
.
1{2 challenges(where: { or: [{ number_gte: 100 }, { outcome: "succeeded" }] }) {3 challenger4 outcome5 application {6 id7 }8 }9}
Observação: Ao construir queries, é importante considerar o impacto do desempenho do operador or
. O or
pode ser útil para ampliar os resultados da busca, mas também pode ter custos significativos. Um dos maiores problemas com o or
é que pode desacelerar os queries. Isto é porque o or
exige que o banco de dados escaneie através de vários índices, o que pode demorar muito. Para evitar estes problemas, recomendamos que os programadores usem operadores and
em vez de or
sempre que possível. Isto retorna filtragens mais precisas, e pode levar a queries mais rápidos e confiáveis.
Todos os Filtros
Lista completa de sufixos de parâmetro:
1_2_not3_gt4_lt5_gte6_lte7_in8_not_in9_contains10_contains_nocase11_not_contains12_not_contains_nocase13_starts_with14_starts_with_nocase15_ends_with16_ends_with_nocase17_not_starts_with18_not_starts_with_nocase19_not_ends_with20_not_ends_with_nocase
Perceba que alguns sufixos só tem apoio para tipos específicos. Por exemplo, o Boolean
só apoia _not
, _in
, e _not_in
, mas o _
só está disponível para tipos de objeto e interface.
Além disto, os seguintes filtros globais estão disponíveis como parte do argumento where
:
1_change_block(number_gte: Int)
Consultas de viagem no tempo
É possível solicitar o estado das suas entidades não só para o bloco mais recente, que é o padrão, mas também para um bloco arbitrário no passado. O bloco em que acontece um query pode ser especificado pelo seu número, ou pelo seu hash, ao incluir um argumento block
nos campos de nível superior de query.
O resultado de um query assim não mudará com o tempo; por exemplo, queries num certo bloco passado retornarão o mesmo resultado, independente de quando ele for executado. Exceto que, se fizer um query num bloco muito perto do topo da chain, o resultado pode mudar se aquele bloco acabar ausente da chain principal e a chain for reorganizada. Quando um bloco puder ser considerado final, o resultado do query não mudará.
Observação: a implementação atual ainda está sujeita a certas limitações que podem violar estas garantias. A implementação nem sempre percebe que um hash de bloco não está na chain principal, ou que um query por hash de bloco retorna um bloco que não pode ser considerado final, mas que pode ser influenciado por uma reorganização de bloco executada concorrente com a solicitação. Elas não afetam os resultados de queries por hash de bloco quando o bloco é final e tem sua presença conhecida na chain principal. Este inquérito explica estas limitações em detalhes.
Exemplo
1{2 challenges(block: { number: 8000000 }) {3 challenger4 outcome5 application {6 id7 }8 }9}
Este query retornará entidades Challenge
e as suas entidades Application
associadas, já que existiram diretamente após processar o bloco de número 8.000.000.
Exemplo
1{2 challenges(block: { hash: "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c" }) {3 challenger4 outcome5 application {6 id7 }8 }9}
Este query retornará entidades Challenge
e as suas entidades Application
associadas, já que existiram diretamente após processar o bloco com o hash indicado.
Consultas de Busca Fulltext
Campos de busca em full-text fornecem uma API de busca de texto expressiva, que pode ser adicionada e personalizada ao schema do subgraph. Para adicionar buscas em full-text ao seu subgraph, veja Como Definir Campos de Busca em Full-Text.
Buscas em full-text têm um campo obrigatório, text
, para ofertar termos de busca. Vários operadores especiais de full-text estão disponíveis para uso neste campo de busca text
.
Operadores de busca fulltext:
Símbolo | Operador | Descrição |
---|---|---|
& | And | Para combinar múltiplos termos de busca num filtro para entidades que incluem todos os termos fornecidos |
| | Or | Consultas com vários termos de busca separados pelo operador or retornarão todas as entidades com uma correspondência de qualquer termo providenciado |
<-> | Follow by | Especifica a distância entre duas palavras. |
:* | Prefix | Use o prefixo para encontrar palavras que correspondem a tal prefixo (2 caracteres necessários.) |
Exemplos
Com o operador or
, esta consulta filtrará para entidades de blog com variações de “anarchism” ou “crumpet” nos seus campos fulltext.
1{2 blogSearch(text: "anarchism | crumpets") {3 id4 title5 body6 author7 }8}
O operador follow by
especifica palavras separadas por uma distância específica nos documentos fulltext. A query a seguir retornará todos os blogs com variações de “decentralize” (“descentralizar”) seguido por “philosophy” (“filosofia”)
1{2 blogSearch(text: "decentralized <-> philosophy") {3 id4 title5 body6 author7 }8}
Combine operadores de fulltext para fazer filtros mais complexos. Com um operador de busca pretext combinado com um ‘follow by’, este exemplo de consulta corresponderá todas as entidades de blog com palavras que começam com “lou” seguido de “music”.
1{2 blogSearch(text: "lou:* <-> music") {3 id4 title5 body6 author7 }8}
Validação
O Graph Node implementa validações baseadas em especificação dos queries da GraphQL que recebe, através do graphql-tools-rs — que tem base na referência de implementação graphql-js. Queries que não aderem a uma regra de validação fazem isso com um erro comum — para saber mais, visite as especificações da GraphQL.
Schema
O schema dos seus dataSources — por exemplo, os tipos de entidade, valores, e conexões que podem ser solicitados num query — é definido através da Linguagem de Definição de Interface da GraphQL (IDL).
Os schemas GraphQL geralmente definem tipos de origem para queries
(solicitações), subscriptions
(inscrições) e mutations
(mutações). O The Graph só apoia queries
. A origem Query
para o seu subgraph é gerada automaticamente a partir do schema GraphQL incluído no manifest do seu subgraph.
Nota: A nossa API não expõe mutações, porque esperamos que os programadores emitam transações diretamente dos seus aplicativos perante a blockchain subjacente.
Entidades
Todos os tipos GraphQL com diretivos @entity
no seu schema serão tratados como entidades, e devem ter um campo ID
.
Observação: Atualmente, todos os tipos no seu schema devem ter um diretivo @entity
. No futuro, trataremos tipos sem um diretivo @entity
como objetos de valor, mas ainda não há apoio a isto.
Metadados de Subgraph
Todos os subgraphs devem ter um objeto _Meta_
gerado automaticamente, que permite acesso aos metadados do subgraph. Isto pode ser solicitado num query como o query mostrado a seguir:
1{2 _meta(block: { number: 123987 }) {3 block {4 number5 hash6 timestamp7 }8 deployment9 hasIndexingErrors10 }11}
Se um bloco for fornecido, os metadados começam naquele bloco; e se não, é usado o último indexado. Se providenciado, o bloco deve ser após o inicial do subgraph, e menor ou igual ao bloco indexado mais recentemente.
deployment
é uma ID única, correspondente ao CID PIFS do arquivo subgraph.yaml
.
O block
fornece informações sobre o bloco mais recente (em consideração a quaisquer limites de bloco passados ao _meta
):
- hash: o hash do bloco
- number: o número do bloco
- timestamp: a hora do bloco, se disponível (disponível atualmente apenas para subgraphs que indexam redes EVM)
hasIndexingErrors
é um boolean que identifica se o subgraph encontrou erros de indexação em algum bloco passado