Hva er GraphQL?

Oversikt

GraphQL er en ny og spennende API for ad hoc spørringer og manipulering. Det er ekstremt fleksibelt og gir mange fordeler. Det er spesielt egnet for å eksponere data som er organisert som grafer og trær. Facebook utviklet GraphQL i 2012 og åpnet den i 2015. 

Det tok raskt av og ble en av de hotteste teknologiene. Mange innovative bedrifter vedtatt og brukte GraphQL i produksjon. I denne opplæringen lærer du: 

  • prinsippene for GraphQL
  • hvordan det sammenlignes med REST
  • hvordan å utforme skjemaer
  • hvordan å konfigurere en GraphQL-server
  • hvordan implementere spørringer og mutasjoner 
  • og noen få flere avanserte emner

Hvor er GraphQL Shine?

GraphQL er på sitt beste når dataene dine er organisert i et hierarki eller en graf, og den forreste delen ønsker å få tilgang til forskjellige delsett i dette hierarkiet eller grafen. Vurder et program som avslører NBA. Du har lag, spillere, trenere, mesterskap, og mye informasjon om hver enkelt. Her er noen eksempler på spørringer:

  • Hva er navnene på spillerne på den nåværende liste over Golden State Warriors?
  • Hva er navnene, høydene og aldrene til starterne til Washington Wizards?
  • Hvilken aktiv trener har mest mesterskap?
  • For hvilke lag og i hvilke år vinner treneren sitt mesterskap?
  • Hvilken spiller vant flest MVP-priser?

Jeg kunne komme opp med hundrevis av slike spørsmål. Tenk deg at du må designe en API for å avsløre alle disse spørringene til forsiden og kunne enkelt utvide API-en med nye søketyper ettersom brukerne eller produktbehandleren kommer opp med nye spennende ting å spørre om.

Dette er ikke trivielt. GraphQL ble utviklet for å løse dette nøyaktige problemet, og med et enkelt API-endepunkt gir det enorm kraft, som du vil se snart.

GraphQL vs REST

Før du drar inn i mutter og bolter i GraphQL, la oss sammenligne den mot REST, som for tiden er den mest populære typen web API.

REST følger en ressursorientert modell. Hvis våre ressurser er spillere, trenere og lag så vil det nok være endepunkter som:

  • / spillere 
  • / spillere / 
  • / trenere
  • / trenere / 
  • / team
  • / lag /

Ofte returnerer endepunktene uten id bare en liste over ids, og sluttpunktene med id returnerer hele informasjonen på en ressurs. Du kan selvfølgelig designe API-en din på andre måter (for eksempel / slutten av spilleren kan også returnere navnet til hver spiller eller all informasjon om hver spiller).

Problemet med denne tilnærmingen i et dynamisk miljø er at du enten underhenter (for eksempel får du bare ids og trenger mer informasjon) eller du overhenter (for eksempel å få full informasjon om hver spiller når du er bare interessert i navnet). 

Det er vanskelige problemer. Når du henter, hvis du henter 100 ids, må du utføre 100 separate API-anrop for å få informasjonen om hver spiller. Når du overhaler, slipper du mye back-end tid og nettverk båndbredde forbereder og overfører mye data som ikke trengs.

Det er måter å adressere det med REST. Du kan designe mange skreddersydde endepunkter, hver og en returnerer nøyaktig de dataene du trenger. Denne løsningen er ikke skalerbar. Det er vanskelig å holde APIen konsistent. Det er vanskelig å utvikle det. Det er vanskelig å dokumentere og bruke det. Det er vanskelig å vedlikeholde det når det er mye overlapping mellom de skreddersydde endepunktene.

Vurder disse ytterligere endpoengene:

  • / spillere / navn
  • / spillere / names_and_championships
  • / lag / startere

En annen tilnærming er å holde et lite antall generiske sluttpunkter, men gi mange spørringsparametere. Denne løsningen unngår det mange endepointsproblemet, men det går mot kornet i REST-modellen, og det er også vanskelig å utvikle og vedlikeholde konsekvent.

Du kan si at GraphQL har tatt denne tilnærmingen til grensen. Det tenker ikke når det gjelder veldefinerte ressurser, men i stedet for undergrafer av hele domenet.

The GraphQL Type System

GraphQL modeller domenet ved hjelp av et type system som består av typer og attributter. Hver attributt har en type. Attributtypen kan være en av de grunnleggende typene som GraphQL gir som ID, String og Boolean, eller en brukerdefinert type. Nodene i grafen er de brukerdefinerte typene, og kantene er attributter som har brukerdefinerte typer. 

Hvis en "Spiller" -type for eksempel har et "lag" -attributt med "Lag" -typen, betyr det at det er en kant mellom hver spillerknude til en lagknude. Alle typer er definert i et skjema som beskriver GraphQL-domeneobjektmodellen. 

Her er et veldig forenklet skjema for NBA-domenet. Spilleren har et navn, et lag han er mest tilknyttet (ja, jeg vet at spillere noen ganger beveger seg fra ett lag til et annet), og antall mesterskap spilleren vant. 

Laget har et navn, en rekke spillere, og antall mesterskap laget vant.

type spiller id: id navn: streng! lag: lag! mesterskapstall: Integer!  Skriv Lag id: ID navn: String! spillere: [spiller!]! mesterskapstall: Integer!  

Det er også forhåndsdefinerte inngangspunkter. Det er spørring, mutasjon og abonnement. Frontenden kommuniserer med bakenden gjennom inngangspunkter og tilpasser dem etter behov.

Her er en spørring som bare returnerer alle spillere:

skriv forespørsel allPlayers: [Player!]! 

Utropstegnet betyr at verdien ikke kan være null. I tilfelle av allPlayers spørring, det kan returnere en tom liste, men ikke null. Det betyr også at det ikke kan være nullspillere på listen (fordi den inneholder spiller!).

Sette opp en GraphQL-server

Her er en fullverdig GraphQL-server basert på node-express. Den har en innebygd hardkodet datalager. Vanligvis vil dataene være i en database eller hentet fra en annen tjeneste. Dataene er definert her (unnskyld på forhånd hvis favorittlaget eller spilleren ikke gjorde det):

la data = "allPlayers": "1": "id": "1", "navn": "Stephen Curry", "championshipCount": 2, "teamId": "3", "2" "id": "2", "navn": "Michael Jordan", "championshipCount": 6, "teamId": "1", "3": "id": "3", "navn" "Scottie Pippen", "championshipCount": 6, "teamId": "1", "4": "id": "4", "navn": "Magic Johnson", "championshipCount": 5, "teamId "5": "5": "5", "navn": "Kobe Bryant", "championshipCount": 5, "teamId": "2", "6": " id ":" 6 "," navn ":" Kevin Durant "," championshipCount ": 1," teamId ":" 3 "," allTeams ": " 1 ": " id ":" 1 " "navn": "Chicago Bulls", "championshipCount": 6, "spillere": [], "2": "id": "2", "navn": "Los Angeles Lakers", "championshipCount" 16, "spillere": [], "3": "id": "3", "navn": "Golden State Warriors", "championshipCount": 5, "spillere": [] 

Bibliotekene jeg bruker er:

const express = kreve ('express'); const graphqlHTTP = krever ('express-graphql'); const app = express (); const buildSchema = krever ('graphql'); const _ = krever (lodd / kjerne);

Dette er koden for å bygge skjemaet. Legg merke til at jeg har lagt til et par variabler til allPlayers root spørring.

skjema = buildSchema ('type Spiller id: ID navn: String! championshipCount: Int! lag: Lag! type Lag id: ID navn: String! championshipCount: Int! spillere: [Player!]! type Spørring allPlayers (forskyvning: Int = 0, grense: Int = -1): [Player!]! ' 

Her kommer nøkkelen: Koble opp spørsmålene og faktisk betjene dataene. De rootValue Objektet kan inneholde flere røtter. 

Her er det bare allPlayers. Det trekker ut kompensasjonen og begrenser fra argumenter, skiver alle spillerdata, og setter deretter laget på hver spiller basert på lag-id. Dette gjør hver spiller til en nestet objekt.

rootValue = allPlayers: (args) => offset = args ['offset'] limit = args ['grense'] r = _.values ​​(data ["allPlayers"]). 1) r = r.slice (0, Math.min (grense, r.length)) _.forEach (r, (x) => data.allPlayers [x.id] .team = data.allTeams [ x.teamId]) return r, 

Endelig er her graphql sluttpunkt, passerer skjemaet og rotverdierobjektet:

app.use ('/ graphql', graphqlHTTP (skjema: skjema, rootValue: rootValue, graphiql: true)); app.listen (3000); module.exports = app;

Innstilling graphiql til ekte gjør det mulig for oss å teste serveren med en fantastisk grafikk IDE i nettleseren. Jeg anbefaler det sterkt for å eksperimentere med ulike søk.

Ad hoc-spørringer med GraphQL

Alt er satt. La oss navigere til http: // localhost: 3000 / graphql og ha det gøy.

Vi kan starte enkle, med bare en liste over spillernes navn:

spørre justNames allPlayers name Utgang: "data": "allPlayers": ["navn": "Stephen Curry", "navn": "Michael Jordan", "navn": "Scottie Pippen ", " navn ":" Magic Johnson ", " navn ":" Kobe Bryant ", " navn ":" Kevin Durant "] 

Ok. Vi har noen superstjerner her. Ingen tvil. La oss gå etter noe mer ferskt: starter fra offset 4 får 2 spillere. For hver spiller, returnere navnet sitt og hvor mange mesterskap de vant, så vel som deres lagnavn og hvor mange mesterskapet laget vant.

spørre toPlayers allPlayers (offset: 4, limit: 2) name championshipCount team name championshipCount Utdata: "data": "allPlayers": ["" navn ":" Kobe Bryant "," championshipCount " 5, "lag": "navn": "Los Angeles Lakers", "championshipCount": 16, "navn": "Kevin Durant", "championshipCount": 1, "lag" "Golden State Warriors", "championshipCount": 5] 

Så Kobe Bryant vant fem mesterskap med Lakers, som vant 16 mesterskap totalt. Kevin Durant vant bare ett mesterskab med Warriors, som vant fem mesterskap totalt.

GraphQL-mutasjoner

Magic Johnson var sikkert en tryllekunstner på banen. Men han kunne ikke ha gjort det uten sin venn Kareem Abdul-Jabbar. La oss legge til Kareem i vår database. Vi kan definere GraphQL-mutasjoner for å utføre operasjoner som å legge til, oppdatere og fjerne data fra grafen vår.

Først la vi legge til en mutasjonstype til skjemaet. Det ser litt ut som en funksjons signatur:

type Mutation createPlayer (navn: String, mesterskapKon: Int, teamId: String): Spiller

Da må vi implementere den og legge den til rotverdien. Implementeringen tar bare parametrene som leveres av spørringen, og legger til et nytt objekt i data [ ''] allPlayers. Det sørger også for å sette laget riktig. Til slutt returnerer den den nye spilleren.

 createPlayer: (args) => id = (_.values ​​(data ['allPlayers']). lengde + 1) .toString () args ['id'] = id args ['team'] = data ['allTeams '] [args [' teamId ']] data [' allPlayers '] [id] = args returnere data [' allPlayers '] [id],

For å faktisk legge til Kareem, kan vi påberope mutasjonen og spørre den returnerte spilleren:

mutation addKareem createPlayer (navn: "Kareem Abdul-Jabbar", mesterskapetall: 6, teamId: "2") name championshipCount team name Utdata: "data": "createPlayer": "name" "Kareem Abdul-Jabbar", "championshipCount": 6, "team": "navn": "Los Angeles Lakers" 

Her er en mørk liten hemmelighet om mutasjoner ... de er faktisk akkurat det samme som spørringer. Du kan endre dataene dine i en spørring, og du kan bare returnere data fra en mutasjon. GraphQL kommer ikke til å se på koden din. Begge spørringer og mutasjoner kan ta argumenter og returnere data. Det er mer som syntaktisk sukker for å gjøre skjemaet mer menneskelig lesbart.

Avanserte emner

abonnementer

Abonnement er en annen killerfunksjon i GraphQL. Med abonnementer kan klienten abonnere på hendelser som vil bli avfyrt når serverstatusen endres. Abonnementer ble introdusert på et senere tidspunkt og implementeres av ulike rammer på ulike måter.

Validering

GraphQL vil verifisere alle forespørsler eller mutasjoner mot skjemaet. Dette er en stor seier når inngangsdata har en kompleks form. Du trenger ikke å skrive irriterende og sprø valideringskode. GraphQL vil ta vare på det for deg. 

Skjema Introspeksjon

Du kan inspisere og spørre nåværende skjema selv. Det gir deg metakrefter for å dynamisk oppdage skjemaet. Her er en spørring som returnerer alle typene navn og deres beskrivelse:

spørring q __schema typer navnbeskrivelse

Konklusjon

GraphQL er en spennende ny API-teknologi som gir mange fordeler over REST APIer. Det er et levende samfunn bak det, for ikke å nevne Facebook. Jeg forutser at det vil bli en front-end stift på kort tid. Gi det et forsøk. Du vil like det.