GraphQL är ett nytt och spännande API för ad hoc-frågor och manipulation. Det är extremt flexibelt och ger många fördelar. Det är särskilt lämpligt för att exponera data organiserad som grafer och träd. Facebook utvecklade GraphQL 2012 och öppnade den 2015.
Det tog snabbt av och blev en av de hetaste teknikerna. Många innovativa företag antog och använde GraphQL i produktion. I denna handledning lär du dig:
GraphQL är bäst när dina data är organiserade i en hierarki eller ett diagram och den främre delen skulle vilja komma åt olika delsatser i denna hierarki eller graf. Tänk på en ansökan som avslöjar NBA. Du har lag, spelare, tränare, mästerskap och mycket information om var och en. Här är några exempelfrågor:
Jag kunde komma med hundratals sådana frågor. Föreställ dig att du måste utforma ett API för att avslöja alla dessa frågor i frontänden och kunna enkelt utöka API: n med nya frågetyper, eftersom dina användare eller produktchefen får nya spännande saker att fråga.
Detta är inte trivialt. GraphQL har utformats för att ta itu med detta exakta problem, och med en enda API-ändpunkt ger den enorm kraft, som du kommer att se snart.
Innan du dyker in i GraphQLs muttrar och bultar, låt oss jämföra den mot REST, som för närvarande är den mest populära typen av web API.
REST följer en resursorienterad modell. Om våra resurser är spelare, tränare och lag så kommer det förmodligen att vara slutpunkter som:
Ofta returnerar endpointsna utan id bara en lista med ids, och slutpunkterna med id returnerar hela informationen på en resurs. Du kan självklart utforma ditt API på andra sätt (till exempel / slutpunkten för spelare kan returnera även namnet på varje spelare eller all information om varje spelare).
Problemet med detta tillvägagångssätt i en dynamisk miljö är att du antingen hämtar (t.ex. du får bara idsen och behöver mer information) eller om du hämtar (t.ex. få full information om varje spelare när du är bara intresserad av namnet).
Det är svåra problem. När du hämtar, om du hämtar 100 ids måste du utföra 100 separata API-samtal för att få informationen om varje spelare. När överhämtning sparar du mycket back-end-tid och nätverksbandbredd som förbereder och överför mycket data som inte behövs.
Det finns sätt att ta itu med REST. Du kan designa många skräddarsydda ändpunkter, var och en återkommer exakt de data du behöver. Denna lösning är inte skalbar. Det är svårt att hålla APIen konsekvent. Det är svårt att utveckla det. Det är svårt att dokumentera och använda det. Det är svårt att behålla det när det finns mycket överlapp mellan de skräddarsydda ändpunkterna.
Tänk på dessa ytterligare slutpunkter:
Ett annat tillvägagångssätt är att hålla ett litet antal generiska slutpunkter, men ge många sökparametrar. Denna lösning undviker de många ändpunkterna, men det går emot REST-modellen, och det är också svårt att utvecklas och upprätthålla konsekvent.
Du kan säga att GraphQL har tagit detta tillvägagångssätt till gränsen. Den tänker inte i form av väldefinierade resurser, utan i stället när det gäller delområden av hela domänen.
GraphQL modellerar domänen med ett typsystem som består av typer och attribut. Varje attribut har en typ. Attributstypen kan vara en av de grundläggande typerna som GraphQL tillhandahåller som ID, String och Boolean, eller en användardefinierad typ. Noderna i grafen är de användardefinierade typerna, och kanterna är attribut som har användardefinierade typer.
Om en "Player" -typ exempelvis har ett "team" -attribut med "Team" -typen betyder det att det finns en kant mellan varje spelarknut till en lagnod. Alla typer definieras i ett schema som beskriver GraphQL-domänobjektmodellen.
Här är ett mycket förenklat schema för NBA-domänen. Spelaren har ett namn, ett lag han är mest associerad med (ja, jag vet att spelare ibland flyttar från ett lag till ett annat) och antalet mästerskapen som spelaren vann.
Laget har ett namn, en rad spelare och antalet mästerskap laget vann.
typ spelare id: id namn: string! lag: lag! mästerskapCount: Integer! Skriv Team id: ID-namn: String! spelare: [spelare!]! mästerskapCount: Integer!
Det finns också fördefinierade ingångspunkter. Det är fråga, mutation och prenumeration. Frontänden kommunicerar med baksidan genom ingångspunkterna och anpassar dem för sina behov.
Här är en fråga som helt enkelt återvänder alla spelare:
skrivfråga allPlayers: [Player!]!
Utropstecken betyder att värdet inte kan vara null. I fallet med allPlayers
fråga kan det returnera en tom lista, men inte noll. Det betyder också att det inte finns någon nollspelare i listan (eftersom den innehåller Player!).
Här är en fullfjädrad GraphQL-server baserad på nod-express. Den har en hårdkodad datalagring i minnet. Normalt kommer data att vara i en databas eller hämtas från en annan tjänst. Uppgifterna definieras här (ursäkta i förväg om ditt favoritlag eller spelare inte gjorde det):
låt data = "allPlayers": "1": "id": "1", "namn": "Stephen Curry", "championshipCount": 2, "teamId": "3", "2" "id": "2", "namn": "Michael Jordan", "championshipCount": 6, "teamId": "1", "3": "id": "3", "namn": "Scottie Pippen", "ChampionshipCount": 6, "teamId": "1", "4": "id": "4", "namn": "Magic Johnson", "championshipCount": 5, "teamId "5": "5": "5", "namn": "Kobe Bryant", "championshipCount": 5, "teamId": "2", "6": " id ":" 6 "," namn ":" Kevin Durant "," championshipCount ": 1," teamId ":" 3 "," allTeams ": " 1 ": " id ":" 1 " "namn": "Chicago Bulls", "championshipCount": 6, "spelare": [], "2": "id": "2", "namn": "Los Angeles Lakers", "championshipCount" 16, "spelare": [], "3": "id": "3", "namn": "Golden State Warriors", "championshipCount": 5, "spelare": []
De bibliotek jag använder är:
const express = kräver ("express"); const graphqlHTTP = kräver ('express-graphql'); const app = express (); const buildSchema = kräver ('graphql'); const _ = kräver ("lodash / core");
Detta är koden för att bygga schemat. Observera att jag har lagt till ett par variabler till allPlayers
root fråga.
schema = buildSchema ('typ Player id: ID-namn: String! championshipCount: Int! lag: Lag! typ Team id: ID-namn: String! championshipCount: Int! spelare: [Player!]! typfråga allPlayers (förskjutning: Int = 0, gräns: Int = -1): [Player!]! '
Här kommer nyckeln: ansluta frågorna och faktiskt betjäna data. De rootValue
Objektet kan innehålla flera rötter.
Här finns det bara allPlayers
. Det extraherar offset och begränsar från argumenten, skivar alla spelarens data och ställer sedan laget på varje spelare baserat på lag-id. Detta gör varje spelare ett kapslade objekt.
rootValue = allPlayers: (args) => offset = args ['offset'] limit = args [' limit '] r = _.values (data ["allPlayers"]). 1) r = r.slice (0, Math.min (gräns, r.längd)) _.forEach (r, (x) => data.allPlayers [x.id] .team = data.allTeams [ x.teamId]) returnera r,
Slutligen är här graphql
slutpunkt, passerar schemat och rotvärdesobjektet:
app.use ('/ graphql', graphqlHTTP (schema: schema, rootValue: rootValue, graphiql: true)); app.listen (3000); module.exports = app;
Miljö graphiql
till Sann
gör det möjligt för oss att testa servern med en fantastisk GraphQL IDE i webbläsaren. Jag rekommenderar det starkt för att experimentera med olika frågor.
Allt är klart. Låt oss navigera till http: // localhost: 3000 / graphql och ha lite roligt.
Vi kan börja enkelt med bara en lista över spelarnamn:
förfrågan justNames allPlayers name Utdata: "data": "allPlayers": ["namn": "Stephen Curry", "namn": "Michael Jordan", "namn": "Scottie Pippen ", " namn ":" Magic Johnson ", " namn ":" Kobe Bryant ", " namn ":" Kevin Durant "]
OK. Vi har några superstjärnor här. Ingen tvekan. Låt oss gå till något snyggare: från offset 4 får 2 spelare. För varje spelare, returnera deras namn och hur många mästerskap de vann samt deras lagnamn och hur många mästerskap laget vann.
fråga två spelare allPlayers (offset: 4, limit: 2) namnmästerskapCount lag name championshipCount Utgång: "data": "allPlayers": ["name": "Kobe Bryant", "championshipCount" 5, "lag": "namn": "Los Angeles Lakers", "championshipCount": 16, "namn": "Kevin Durant", "championshipCount": 1, "lag" "Golden State Warriors", "ChampionshipCount": 5]
Så vann Kobe Bryant fem mästerskap med Lakers, som vann totalt 16 mästerskap. Kevin Durant vann bara ett mästerskap med Warriors, som vann fem mästerskap totalt.
Magic Johnson var visserligen en trollkarl på domstolen. Men han kunde inte ha gjort det utan sin vän Kareem Abdul-Jabbar. Låt oss lägga till Kareem i vår databas. Vi kan definiera GraphQL-mutationer för att utföra operationer som att lägga till, uppdatera och ta bort data från vår graf.
Låt oss först lägga till en mutationstyp i schemat. Det ser lite ut som en funktionssignatur:
typ mutation createPlayer (namn: String, championshipCount: Int, teamId: String): Player
Då måste vi implementera det och lägga till det i root-värdet. Genomförandet tar helt enkelt parametrarna som tillhandahålls av frågan och lägger till ett nytt objekt i Data [ 'allPlayers']
. Det säkerställer också att teamet är korrekt. Slutligen returnerar den den nya spelaren.
createPlayer: (args) => id = (_.values (data ['allPlayers']). längd + 1) .toString () args ['id'] = id args ['team'] = data ['allTeams '] [args [' teamId ']] data [' allPlayers '] [id] = args returnera data [' allPlayers '] [id],
För att faktiskt lägga till Kareem kan vi åberopa mutationen och fråga den återvändande spelaren:
mutation addKareem createPlayer (namn: "Kareem Abdul-Jabbar", mästerskapets nummer: 6, lagId: "2") namnmästerskapCount lag name Utgång: "data": "createPlayer": "name": "Kareem Abdul-Jabbar", "championshipCount": 6, "lag": "namn": "Los Angeles Lakers"
Här är en mörk liten hemlighet om mutationer ... de är faktiskt exakt samma som frågor. Du kan ändra dina uppgifter i en fråga, och du kan bara returnera data från en mutation. GraphQL kommer inte att kika in i din kod. Både frågor och mutationer kan ta argument och returnera data. Det är mer som syntaktiskt socker för att göra ditt schema mer mänskligt läsbart.
Prenumerationer är en annan mördarefunktion i GraphQL. Med abonnemang kan kunden prenumerera på händelser som kommer att avfyras när serverns tillstånd ändras. Prenumerationer infördes senare och implementeras genom olika ramar på olika sätt.
GraphQL verifierar varje fråga eller mutation mot schemat. Detta är en stor seger när ingångsdata har en komplex form. Du behöver inte skriva irriterande och spröd valideringskod. GraphQL tar hand om det för dig.
Du kan inspektera och fråga det aktuella schemat själv. Det ger dig metakraft för att dynamiskt upptäcka schemat. Här är en fråga som returnerar alla typnamn och deras beskrivning:
fråga q __schema typer namnbeskrivning
GraphQL är en spännande ny API-teknik som ger många fördelar över REST API. Det finns en livlig gemenskap bakom det, för att inte tala om Facebook. Jag förutspår att det kommer att bli en front-end häftning på nolltid. Ge det ett försök. Du kommer gilla det.