Beste praksis
Her beskriver vi beste praksis for utvikling av GraphQL-APIer. Dette er føringer som de fleste burde følge.
Paginering
Det er anbefalt å bruke paginering for alle Query-felter som returnerer lister av objekter.
Man kan fravike bruk av paginering i tilfeller der man har svært god kontroll på at antallet elementer som kan returneres er lavt.
En god tommelfinger-regel er at vi bruker paginering om det kan være mer enn 10 elementer.
Paginering skal gjøres med Cursors slik det er beskrevet på prinsipp-siden.
Dersom klienter har behov for side-basert navigering så bør dette løses ved hjelp av cached page boundaries.
Dette gjør vi ved å legge til et ekstra felt på den relevante Connection-typen som gir oss en liste med sider.
Gitt for eksempel personsøket fra prinsipp-siden, så kan vi utvide PersonConnection-typen som følger:
type Query {
personer(first: Int, after: String): PersonConnection
}
type PersonConnection {
...
"""The cursors for the first 10 pages of the result."""
pages: [Page]
}
type Page {
number: Int
cursor: String
}
Basert på denne kan for eksempel frontend tegne opp en komponent med sidene fra treffet.
Siden brukeren står på finner man ved å sammenligne PageInfo.startCursor med cursor-verdien fra de respektive sidene.
Trykker brukeren på en av sidene så utfører vi et nytt søk med after-argumentet satt til cursor-verdien fra den valgte siden.
Nullability
Det er anbefalt å la alle felt i skjema være nullable med mindre typen feltet står på ikke gir mening uten denne verdien. Grunnen til dette er at hele objektet må settes til null dersom et non-nullable felt settes til null. Dette kan for eksempel skje dersom det oppstår en uforutsett feil i systemet, eller API-brukeren ikke har tilgang.
Se semantic nullability for mer informasjon om problematikken. Denne RFCen inneholder en god beskrivelse av både problemet, samt mulige retninger som vurderes for å forbedre situasjonen fremover.
Ett av tiltakene som finnes og som vi allerede eksperimenterer med i dag er @semanticNonNull-direktivet. Dette lar oss kommunisere til klienten at et felt kun settes til null dersom det har oppstått en feil. Vi har lagt til dette i opptakssubgrafen og vurderer å gjøre det samme i de andre subgrafene. Frontend planlegger å teste om støtten for dette i GraphQL Codegen gir verdi.
Bare felt på Mutation-typen kan utføre endringer
Det er kun toppnivåfelt på Mutation-typen som kan utføre endringer i de underliggende systemene.
Alle andre felt må være idempotent/etterlate systemet i samme tilstand som det var i.
Dette følger av GraphQL-spesifikasjonen:
Normally the executor can execute the entries in a grouped field set in whatever order it chooses (normally in parallel). Because the resolution of fields other than top-level mutation fields must always be side effect-free and idempotent, the execution order must not affect the result, and hence the service has the freedom to execute the field entries in whatever order it deems optimal.
Dette innebærer at design ala følgende ikke er lovlig:
type PersonMutation {
oppdaterNavn(input: PersonOppdaterNavnInput): PersonOppdaterNavnPayload
oppdaterAdresse(input: PersonOppdaterAdresseInput): PersonOppdaterAdressePayload
oppdaterKjonn(input: PersonOppdaterKjonnInput): PersonOppdaterKjonnPayload
}
I stedet må mutasjonsfeltene være på Mutation-typen:
type Mutation {
oppdaterPersonNavn(input: PersonOppdaterNavnInput): PersonOppdaterNavnPayload
oppdaterPersonAdresse(input: PersonOppdaterAdresseInput): PersonOppdaterAdressePayload
oppdaterPersonKjonn(input: PersonOppdaterKjonnInput): PersonOppdaterKjonnPayload
}