Querying > Bästa praxis för förfrågningar

Bästa praxis för förfrågningar

Reading time: 9 min

The Graph tillhandahåller ett decentraliserat sätt att hämta data från blockkedjor.

The Graph-nätverkets data exponeras genom ett GraphQL API, vilket gör det enklare att fråga data med GraphQL-språket.

Den här sidan kommer att guida dig genom de grundläggande reglerna för GraphQL-språket och bästa praxis för GraphQL-frågor.


Att fråga ett GraphQL API

Länk till detta avsnitt

Anatomien av en GraphQL-fråga

Länk till detta avsnitt

Till skillnad från REST API bygger ett GraphQL API på ett schema som definierar vilka frågor som kan utföras.

Till exempel kommer en fråga för att hämta en token med hjälp av frågan token att se ut som följer:

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

som kommer att returnera följande förutsägbara JSON-svar (när du skickar rätt $id variabelvärde):

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

GraphQL-frågor använder GraphQL-språket, som definieras i en specifikation.

Ovanstående GetToken-fråga består av flera språkdelar (ersätts nedan med [...] platshållare):

query [operationName]([variableName]: [variableType]) {
[queryName]([argumentName]: [variableName]) {
# "{ ... }" uttrycker en Selection-Set, vi frågar efter fält från `queryName`.
[field]
[field]
}
}

Listan över syntaktiska do's and don'ts är lång, men här är de viktigaste reglerna att tänka på när det gäller att skriva GraphQL-förfrågningar:

  • Varje queryName får endast användas en gång per operation.
  • Varje field får bara användas en gång i ett urval (vi kan inte fråga id två gånger under token)
  • Some fields or queries (like tokens) return complex types that require a selection of sub-field. Not providing a selection when expected (or providing one when not expected - for example, on id) will raise an error. To know a field type, please refer to Graph Explorer.
  • Varje variabel som tilldelas ett argument måste matcha dess typ.
  • I en given lista med variabler måste var och en av dem vara unik.
  • Alla definierade variabler måste användas.

Om du inte följer ovanstående regler kommer du att få ett felmeddelande från Graph API.

For a complete list of rules with code examples, please look at our GraphQL Validations guide.

Att skicka en fråga till ett GraphQL API

Länk till detta avsnitt

GraphQL är ett språk och en uppsättning konventioner som transporteras över HTTP.

Det innebär att du kan ställa en fråga till ett GraphQL API med hjälp av standard fetch (nativt eller via @whatwg-node/fetch eller isomorphic-fetch).

Men, som det anges i "Frågehantering från en applikation", rekommenderar vi att du använder vår graph-client som stöder unika funktioner som:

Så här ställer du en fråga till The Graph med 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` är fullständigt typad!
console.log(result)
}
main()

Fler GraphQL-klientalternativ behandlas i "Querying from an Application".

Nu när vi har gått igenom de grundläggande reglerna för syntax för GraphQL-förfrågningar ska vi titta på bästa praxis för att skriva GraphQL-förfrågningar.


Skriv alltid statiska frågor

Länk till detta avsnitt

En vanlig (dålig) praxis är att dynamiskt bygga upp frågesträngar enligt följande:

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

Medan det tidigare avsnittet genererar en giltig GraphQL-fråga har den många nackdelar:

  • det gör det svårare att förstå frågan som helhet
  • utvecklare är ansvariga för att säkert sanera stränginterpolationen
  • att inte skicka värdena av variablerna som en del av förfrågningsparametrarna förhindrar möjlig cache på servern
  • det hindrar verktyg från statisk analys av frågan (exempel: Linter eller typgenereringsverktyg)

Av dessa skäl rekommenderas det alltid att skriva frågor som statiska strängar:

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,
},
})

Detta medför många fördelar:

  • Lättlästa och underhållna frågor
  • GraphQL server hanterar sanitet av variabler
  • Variabler kan cachas på serversidan
  • Frågor kan statiskt analyseras av verktyg (mer om detta i följande avsnitt)

Observera: Hur man inkluderar fält villkorligt i statiska frågor

Ibland vill vi inkludera fältet owner endast under vissa villkor.

För detta kan vi utnyttja direktivet @include(if:...) på följande sätt:

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,
},
})

Observera: Det motsatta direktivet är @skip(if: ...).

Ask for what you want

Länk till detta avsnitt

GraphQL blev känd för sitt motto "Be om det du vill ha".

Av den anledningen finns det ingen möjlighet i GraphQL att få alla tillgängliga fält utan att behöva lista dem individuellt.

När du frågar GraphQL API:er, tänk alltid på att endast fråga efter de fält som faktiskt kommer att användas.

En vanlig orsak till överhämtning är samlingar av enheter. Som standard kommer frågor att hämta 100 enheter i en samling, vilket vanligtvis är mycket mer än vad som faktiskt kommer att användas, t.ex., för att visas för användaren. Därför bör frågor nästan alltid ange first explicit och se till att de bara hämtar så många enheter som de faktiskt behöver. Detta gäller inte bara för toppnivåsamlingar i en fråga, utan ännu mer för inbäddade samlingar av enheter.

Till exempel, i följande fråga:

query listTokens {
tokens {
# kommer att ge upp till 100 tokens
id
transactions {
# kommer att ge upp till 100 transaktioner
id
}
}
}

Svaret kan innehålla 100 transaktioner för varje av de 100 tokens.

Om applikationen bara behöver 10 transaktioner bör frågan explicit ange first: 10 på transaktionsfältet.

Use a single query to request multiple records

Länk till detta avsnitt

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

Länk till detta avsnitt

Din applikation kan kräva att du ställer flera typer av datafrågor enligt följande:

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)
)

Medan denna implementation är helt giltig kräver den två rundturer med GraphQL API:n.

Lyckligtvis är det också giltigt att skicka flera frågor i samma GraphQL-begäran enligt följande:

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)

Detta tillvägagångssätt kommer att förbättra den övergripande prestandan genom att minska tiden som spenderas på nätverket (sparar en omväg till API:n) och kommer att ge en mer koncis implementation.

Dra nytta av GraphQL-fragment

Länk till detta avsnitt

En användbar funktion för att skriva GraphQL-frågor är GraphQL-fragment.

Om vi tittar på följande fråga kommer du att märka att vissa fält upprepas över flera urvalssatser ({ ... }):

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

Sådana upprepade fält (id, active, status) medför många problem:

  • svårare att läsa för mer omfattande frågor
  • när du använder verktyg som genererar TypeScript-typer baserat på frågor (mer om det i den sista avsnittet), kommer newDelegate och oldDelegate att resultera i två olika inline-gränssnitt.

En omstrukturerad version av frågan skulle vara följande:

query {
bondEvents {
id
newDelegate {
...DelegateItem
}
oldDelegate {
...DelegateItem
}
}
}
# vi definierar ett fragment (subtyp) på Transcoder
# att faktorisera upprepade fält i frågan
fragment DelegateItem on Transcoder {
id
active
status
}

Att använda GraphQL fragment kommer att förbättra läsbarheten (särskilt i större skala) och leda till bättre generering av TypeScript-typer.

När du använder verktyget för typsgenerering kommer den ovanstående frågan att generera en korrekt typ av DelegateItemFragment (se sista avsnittet).

Dos and Don'ts för GraphQL Fragment

Länk till detta avsnitt

Fragmentbas måste vara en typ

Ett fragment kan inte baseras på en oanvändbar typ, kort sagt, på en typ som inte har fält:

fragment MyFragment on BigInt {
# ...
}

BigInt är en skalär (inbyggd "vanlig" typ) som inte kan användas som grund för ett fragment.

Hur man sprider ett fragment

Fragment är definierade på specifika typer och bör användas i enlighet med det i frågor.

Exempel:

query {
bondEvents {
id
newDelegate {
...VoteItem # Fel! `VoteItem` kan inte spridas på `Transcoder` typ
}
oldDelegate {
...VoteItem
}
}
}
fragment VoteItem on Vote {
id
voter
}

newDelegate och oldDelegate är av typen Transcoder.

Det är inte möjligt att sprida ett fragment av typ Vote här.

Definiera fragment som en atomisk affärsenhet för data

GraphQL Fragment måste definieras baserat på deras användning.

För de flesta användningsfall är det tillräckligt att definiera ett fragment per typ (i fallet med upprepade fält eller typgenerering).

Här är en tumregel för användning av fragment:

  • när fält av samma typ upprepas i en fråga, gruppera dem i ett fragment
  • när liknande men inte samma fält upprepas, skapa flera fragment, t.ex.
# base fragment (mostly used in listing)
fragment Voter on Vote {
id
voter
}
# utökat fragment (vid förfrågan om en detaljerad vy av en omröstning)
fragment VoteWithPoll on Vote {
id
voter
choiceID
poll {
id
proposal
}
}

De väsentliga verktygen

Länk till detta avsnitt

Webbaserade GraphQL-upptäckare

Länk till detta avsnitt

Iterating over queries by running them in your application can be cumbersome. For this reason, don't hesitate to use Graph Explorer to test your queries before adding them to your application. Graph Explorer will provide you a preconfigured GraphQL playground to test your queries.

If you are looking for a more flexible way to debug/test your queries, other similar web-based tools are available such as Altair and GraphiQL.

GraphQL Linting

Länk till detta avsnitt

För att hålla dig uppdaterad med de tidigare nämnda bästa praxis och syntaktiska regler rekommenderas det starkt att använda följande arbetsflöde och IDE-verktyg.

GraphQL ESLint

GraphQL ESLint will help you stay on top of GraphQL best practices with zero effort.

Setup the "operations-recommended" config will enforce essential rules such as:

  • @graphql-eslint/fields-on-correct-type: används ett fält på en korrekt typ?
  • @graphql-eslint/no-unused variables: bör en given variabel förbli oanvänd?
  • och mer!

Detta kommer att tillåta dig att upptäcka fel utan ens att testa frågor på lekplatsen eller köra dem i produktion!

VSCode och GraphQL

GraphQL VSCode-tillägget är ett utmärkt komplement till din utvecklingsarbetsflöde för att få:

  • syntaxmarkering
  • autokompletteringsförslag
  • validering mot schema
  • snuttar
  • gå till definition för fragment och inmatningstyper

Om du använder graphql-eslint är ESLint VSCode-tillägget ett måste för att visualisera fel och varningar korrekt infogade i din kod.

WebStorm/Intellij och GraphQL

JS GraphQL-tillägget kommer att förbättra din upplevelse av att arbeta med GraphQL genom att tillhandahålla:

  • syntaxmarkering
  • autokompletteringsförslag
  • validering mot schema
  • snuttar

Mer information om denna WebStorm-artikel som visar upp alla tilläggets huvudfunktioner.

Redigera sida

Tidigare
Hantera dina API-nycklar
Nästa
Att göra förfrågningar från en Applikation
Redigera sida