Querying > Etiqueta de Query

Etiqueta de Query

Reading time: 9 min

O The Graph fornece um método descentralizado de consultar dados de blockchains em query.

Os dados da rede do The Graph são expostos por uma API GraphQL, o que facilita queries de dados com a linguagem GraphQL.

Este é um guia sobre as regras essenciais da linguagem GraphQL, e a etiqueta em queries com GraphQL.


Queries numa API GraphQL

Link para esta seção

A anatomia de um query GraphQL

Link para esta seção

Ao contrário da REST API, uma API GraphQL é construída em cima de um Schema que define quais queries podem ser realizados.

Por exemplo, um query para retornar um token através do query token ficará assim:

query GetToken($id: ID!) {
token(id: $id) {
id
owner
}
}

o que retornará a seguinte resposta previsível no JSON (ao passar o valor variável $id apropriado):

{
"token": {
"id": "...",
"owner": "..."
}
}

Queries GraphQL usam a linguagem GraphQL, definida sobre uma especificação.

O query GetToken acima é composto de várias partes da linguagem (substituído abaixo com [...] temporários):

query [operationName]([variableName]: [variableType]) {
[queryName]([argumentName]: [variableName]) {
# "{ ... }" expressa um Selection-Set, estamos a consultar campos do `queryName`.
[field]
[field]
}
}

Enquanto a lista de sintaxes proibidas e permitidas é longa, veja a seguir as regras essenciais quando se trata de escrever queries GraphQL:

  • Cada queryName deve ser usado apenas uma vez por operação.
  • Cada field deve ser usado apenas uma vez numa seleção (não podemos consultar a id duas vezes sob o token)
  • Alguns fieldss ou queries (como tokens) retornam tipos complexos que requerem uma seleção de subcampo. Não providenciar uma seleção quando esperado (ou providenciar uma quando não é esperado - por exemplo, no id) resultará num erro. Para conhecer um tipo de campo, consulte o Graph Explorer.
  • Qualquer variável apontada a um argumento deve corresponder ao seu tipo.
  • Em uma lista dada de variáveis, cada uma delas deve ser única.
  • Todas as variáveis definidas devem ser usadas.

Não seguir as regras acima causará um erro da API do Graph.

Para uma lista completa de regras com exemplos de código, veja o nosso guia de Validações GraphQL.

Como enviar um query a uma API GraphQL

Link para esta seção

GraphQL é uma linguagem e conjunto de convenções que transportam através do HTTP.

Ou seja, dá para fazer um query numa API GraphQL com o fetch normal (nativamente ou via @whatwg-node/fetch ou isomorphic-fetch).

Porém, como dito em "Queries num Aplicativo", é melhor usar o nosso graph-client, que apoia funções como:

Aqui está como fazer queries para o The Graph com o graph-client:

import { execute } from '../.graphclient'
const query = `
query GetToken($id: ID!) {
token(id: $id) {
id
owner
}
}
`
const variables = { id: '1' }
async function main() {
const result = await execute(query, variables)
// `result` is fully typed!
console.log(result)
}
main()

Falamos sobre mais alternativas de cliente GraphQL em "Queries num Aplicativo".

Agora que cobrimos as regras básicas da sintaxe de queries GraphQL, vamos agora ver como escrever bons queries no GraphQL.


Boas práticas

Link para esta seção

Sempre escreva consultas estáticas

Link para esta seção

É (um erro) comum construir strings de consulta dinamicamente, como a seguir:

const id = params.id
const fields = ['id', 'owner']
const query = `
query GetToken {
token(id: ${id}) {
${fields.join('\n')}
}
}
`
// Execute query...

Enquanto o trecho acima produz um query válido no GraphQL, isto traz muitas desvantagens:

  • ela dificulta a consulta na totalidade
  • os programadores são responsáveis por higienizar com segurança a interpolação de string
  • não mandar os valores das variáveis como parte dos parâmetros de pedido impede um possível caching no lado do servidor
  • ela impede as ferramentas de analisar estaticamente a consulta (por ex. Linter ou ferramentas de geração de tipo)

Por isto, é recomendado sempre escrever queries como strings estáticas:

import { execute } from 'your-favorite-graphql-client'
const id = params.id
const query = `
query GetToken($id: ID!) {
token(id: $id) {
id
owner
}
}
`
const result = await execute(query, {
variables: {
id,
},
})

Isto traz muitas vantagens:

  • Queries fáceis de ler e manter
  • O servidor GraphQL cuida da higienização de variáveis
  • Variáveis podem ser cacheadas no nível do servidor
  • Queries podem ser analisados estaticamente por ferramentas (mais sobre isto nas seções seguintes)

Nota: Como incluir campos condicionalmente em queries estáticos

Talvez você queira incluir o campo owner com uma condição particular.

Para isto, use a diretiva @include(if:...) a seguir:

import { execute } from 'your-favorite-graphql-client'
const id = params.id
const query = `
query GetToken($id: ID!, $includeOwner: Boolean) {
token(id: $id) {
id
owner @include(if: $includeOwner)
}
}
`
const result = await execute(query, {
variables: {
id,
includeOwner: true,
},
})

Nota: a diretiva oposta é @skip(if: ...).

Ask for what you want

Link para esta seção

O GraphQL ficou famoso por sua frase de efeito "pergunte pelo que queres".

Por isto, no GraphQL, não há como obter todos os campos disponíveis sem ter que listá-los individualmente.

Ao consultar APIs GraphQL, sempre considere fazer query apenas dos campos que serão usados.

Over-fetching é normalmente causado pela coleção de entidades. Por natureza, os queries retirarão 100 entidades em uma coleção, muito mais do que realmente será usado; por ex., para fins de amostra ao usuário. Portanto, queries devem quase sempre ser configurados primeiro, com a garantia de que só retirarão quantas entidades forem necessárias. Isto serve não só para coleções de alto nível em uma consulta, mas mais ainda para coleções aninhadas de entidades.

Por exemplo, no query seguinte:

query listTokens {
tokens {
# will fetch up to 100 tokens
id
transactions {
# will fetch up to 100 transactions
id
}
}
}

A resposta pode conter 100 transações para cada um dos 100 tokens.

Se o aplicativo só precisa de 10 transações, o query deve configurar explicitamente first: 10 no campo de transações.

Use a single query to request multiple records

Link para esta seção

By default, subgraphs have a singular entity for one record. For multiple records, use the plural entities and filter: where: {id_in:[X,Y,Z]} or where: {volume_gt:100000}

Example of inefficient querying:

query SingleRecord {
entity(id: X) {
id
name
}
}
query SingleRecord {
entity(id: Y) {
id
name
}
}

Example of optimized querying:

query ManyRecords {
entities(where: { id_in: [X, Y] }) {
id
name
}
}

Combine multiple queries in a single request

Link para esta seção

O seu aplicativo pode exigir queries de múltiplos tipos de dados, como a seguir:

import { execute } from "your-favorite-graphql-client"
const tokensQuery = `
query GetTokens {
tokens(first: 50) {
id
owner
}
}
`
const countersQuery = `
query GetCounters {
counters {
id
value
}
}
`
const [tokens, counters] = Promise.all(
[
tokensQuery,
countersQuery,
].map(execute)
)

Enquanto esta implementação é totalmente válida, ela exigirá duas rondas totais com a API GraphQL.

Felizmente, também vale enviar múltiplos queries no mesmo pedido à GraphQL, como a seguir:

import { execute } from "your-favorite-graphql-client"
const query = `
query GetTokensandCounters {
tokens(first: 50) {
id
owner
}
counters {
id
value
}
}
`
const { result: { tokens, counters } } = execute(query)

Este método melhorará o desempenho geral ao reduzir o tempo gasto na rede (pois, poupa-lhe de uma viagem ao redor da API) e fornecerá implementações mais concisas.

Como Aproveitar Fragmentos GraphQL

Link para esta seção

Uma boa ferramenta para escrever queries GraphQL é o GraphQL Fragment.

No seguinte query, perceba que alguns campos são repetidos em vários Selection-Sets ({ ... }):

query {
bondEvents {
id
newDelegate {
id
active
status
}
oldDelegate {
id
active
status
}
}
}

Estes campos repetidos (id, active, status) trazem muitos problemas:

  • difíceis de ler para consultas mais extensivas
  • ao usar ferramentas que geram tipos TypeScript baseados em queries (mais sobre isto na última seção), newDelegate e oldDelegate retornarão duas interfaces distintas em linha.

Refatorado, o query ficaria assim:

query {
bondEvents {
id
newDelegate {
...DelegateItem
}
oldDelegate {
...DelegateItem
}
}
}
# nós definimos um fragmento (subtipo) no Transcoder
# para fatorizar campos repetidos no query
fragment DelegateItem on Transcoder {
id
active
status
}

Usar o fragment do GraphQL melhorará a legibilidade (especialmente em escala) e também melhorará a geração de tipos TypeScript.

Ao usar a ferramenta de geração de tipos, o query acima gerará um tipo DelegateItemFragment apropriado (veja a última seção "Ferramentas").

O que fazer e o que não fazer em Fragments GraphQL

Link para esta seção

A base do fragment deve ser um tipo

Um Fragment não pode ser baseado num tipo não aplicável; ou seja, um tipo sem campos:

fragment MyFragment on BigInt {
# ...
}

O BigInt é um escalar (tipo "plano" nativo) que não pode ser usado como a base de um fragment.

Como espalhar um Fragment

Fragments são definidos em tipos específicos e devem ser usados de acordo nos queries.

Exemplo:

query {
bondEvents {
id
newDelegate {
...VoteItem # Error! `VoteItem` cannot be spread on `Transcoder` type
}
oldDelegate {
...VoteItem
}
}
}
fragment VoteItem on Vote {
id
voter
}

newDelegate e oldDelegate são do tipo Transcoder.

Não é possível espalhar um fragment do tipo Vote aqui.

Defina o Fragment como uma unidade de negócios atômica de dados

O Fragment GraphQL deve ser definido baseado no seu uso.

Para a maioria dos casos de uso, definir um fragment por tipo (no caso do uso repetido de campos ou geração de tipos) já basta.

Aqui estão algumas regras básicas para o uso de Fragments:

  • quando campos do mesmo tipo repetem em um query, agrupe-os em um Fragment
  • quando campos parecidos (mas não idênticos) repetem, crie múltiplos fragmentos, por ex:
# base fragment (mostly used in listing)
fragment Voter on Vote {
id
voter
}
# extended fragment (when querying a detailed view of a vote)
fragment VoteWithPoll on Vote {
id
voter
choiceID
poll {
id
proposal
}
}

Ferramentas essenciais

Link para esta seção

Exploradores do GraphQL baseados em web

Link para esta seção

Pode ser até chato executar queries no seu aplicativo para iterar sobre elas. Por isto, não hesite em usar o Graph Explorer para testar as suas queries antes de adicioná-las. O Graph Explorer lhe providenciará um ambiente de testes GraphQL pré-configurado para testar as suas queries.

Se procura uma maneira mais flexível de debugar/testar as suas consultas, há outras ferramentas baseadas em web, como Altair e GraphiQL.

GraphQL Linting

Link para esta seção

Para acompanhar as melhores práticas e regras sintáticas ditas acima, vale muito usar o workflow e as ferramentas IDE a seguir.

GraphQL ESLint

O GraphQL ESLint ajudará-lhe a aperfeiçoar as melhores práticas no GraphQL sem nenhum esforço.

Preparar a configuração "operations-recommended" irá impor regras essenciais, como:

  • @graphql-eslint/fields-on-correct-type: um campo está num tipo apropriado?
  • @graphql-eslint/no-unused variables: uma variável usada deve ficar sem uso?
  • e mais!

Isto permitirá-lhe detectar erros até mesmo sem testar queries no playground ou executá-las na produção!

VSCode e GraphQL

A extensão GraphQL VSCode é ótima para o seu workflow de desenvolvimento. Com ela, você consegue:

  • destaque de sintaxe
  • sugestões de autocomplete
  • validação contra schema
  • snippets
  • definições para fragments e tipos de entrada

Se usa o graphql-eslint, a extensão ESLint VSCode é indispensável para visualizar corretamente os erros e avisos em inline no seu código.

WebStorm/Intellij e GraphQL

O plugin JS GraphQL melhorará muito a sua experiência de trabalho com o GraphQL, pois proverá:

  • destaque de sintaxe
  • sugestões de autocomplete
  • validação contra schema
  • snippets

Mais informações neste artigo da WebStorm, que mostra todos os recursos principais do plugin.

Editar página

Anterior
Como gerir as suas chaves de API
Próximo
Como Fazer Queries de um Aplicativo
Editar página