Multiplayer Tic Tac Toe med GraphQL

0
18

GraphQL är ett frågespråk för Api: er som är mycket bättre rustade för front-end-utvecklare. Som GraphQL webbplats förklarar det, du beskriva dina uppgifter, be om vad du vill, och få förutsägbara resultat.

Om du inte har arbetat med det innan, GraphQL kan vara lite förvirrande att grok vid första anblicken. Så, låt oss bygga en multiplayer tic-tac-toe spel som använder det för att visa hur det används och vad vi kan göra med det.

Första vi behöver är en backend för våra Api: er. Vi kommer att använda Hasura GraphQL Motorn tillsammans med en anpassad GraphQL server för denna handledning. Vi ska titta på frågor och mutationer som klienten behöver för att bygga spelet. Du kan genomföra denna typ av sak oavsett i vilken ram du önskar, men vi är igång med att använda Reagera och Apollo för denna tutorial.

Det här är vad vi gör:

GitHub Repo

En kort GraphQL primer

GraphQL är ett frågespråk för Api: er, ett språk med en syntax som definieras ett sätt att hämta data från servern. Det fungerar med någon form av programmeringsgränssnitt (API) som backas upp av ett starkt system som gör din kodbas och fjädrande. Några av de främsta egenskaperna hos GraphQL är:

  • Kunden kan begära servern för vilka frågor som det stöder (kolla in dokumentationen för mer).
  • Kunden måste begära servern för exakt vad den vill ha. Det kan inte be om något som ett jokertecken ( * ), men ganska exakt fält. Till exempel, för att få ett användar-ID och namn, GraphQL fråga skulle vara något i stil med:
    frågan {
    användaren {
    id
    namn
    }
    }
  • Varje sökning som görs till en enda slutpunkt och varje begäran av en POST-begäran.
  • Med tanke på en fråga, struktur svar verkställs. Till exempel, för ovanstående fråga för att få id och namn på en användare objekt, en framgångsrik svar skulle vara något i stil med:
    {
    “data”: {
    “användare”: {
    “id”: “AUTH:482919”,
    “name”: “Jon Doe”
    }
    }
    }

Denna serie av artiklar är en bra plats att börja om du vill veta mer om GraphQL.

Varför använder vi GraphQL, ändå?

Vi bara diskuterade hur GraphQL krav att kunden måste begära servern för exakt vad den vill ha. Det betyder att det finns inga onödiga data hämtas från servern, som i händelse av VILA där du skulle få en enorm respons även när du behöver ett fält. Få vilka nya behov och inte bara vad vi behöver optimerar svar så att de är snabba och förutsägbara.

Kunden kan begära servern för sin schema via introspektion. Detta schema kan användas för att bygga frågor dynamiskt med hjälp av ett API explorer som GraphiQL. Det gör det också möjligt fibersläpp och auto-fyller eftersom varje fråga kan byggas med och kontrolleras mot GraphQL schema. Som en front-end utvecklare, detta förbättrar avsevärt DX eftersom det är mycket mindre mänskliga faktorn inblandad.

Eftersom det finns en enda slutpunkt och varje begäran av en POST-begäran, GraphQL kan undvika en hel del boilerplate, eftersom det inte har att spåra endpoints, begäran nyttolaster och svar typer. Svar caching är mycket lättare eftersom varje fråga-svar kan förväntas vara av en viss struktur.

Dessutom, GraphQL har en väl definierad spec för att genomföra realtid abonnemang. Du behöver inte komma med din egen implementation för att bygga realtids-servrar. Bygga en server som överensstämmer med GraphQL ‘ s real-tid spec och varje kund kan börja använda den i realtid GraphQL Api: er med lätthet.

GraphQL Terminologi

Jag kommer att använda några GraphQL villkoren i detta inlägg, så det är värt att som omfattar några av dem här i förväg.

  • Fråga: En GraphQL frågan är en som helt enkelt hämtar data från servern.
  • Mutation: Detta är en GraphQL fråga som ändrar något på servern och hämtar några data.
  • Prenumeration: Detta är en GraphQL fråga som stöder klienten att några ändringar på servern.
  • Fråga variabler: Detta tillåter oss att lägga till parametrar till en GraphQL fråga.

Att komma tillbaka till backend

Nu när vi har en ytlig förståelse av GraphQL, låt oss börja med att modellera en backend. Våra GraphQL backend skulle vara en kombination av Hasura GraphQL Sökmotor och en egen GraphQL server. Vi ska inte gå in på detaljer i koden i detta fall.

Eftersom Tic Tac Toe är ett multiplayer-spel, det finns ett behov av att lagra stat i databasen. Vi kommer att använda Postgres som finns i vår databas och Hasura GraphQL Motorn som en GraphQL server som tillåter oss att CRUD-data i Postgres över GraphQL.

Bortsett från CRUD på databasen vi vill också köra några egna logik i form av GraphQL mutationer. Vi kommer att använda en anpassad GraphQL server för att.

Hasura beskriver sig själv ganska bra i sin README-filen:

GraphQL Motorn är en blixtsnabb GraphQL server som ger dig omedelbar, realtime GraphQL Api: er över Postgres, med webhook triggers på evenemang databasen och remote scheman för affärslogik.

Gå lite djupare, Hasura GraphQL Engine är ett open-source server som sitter på toppen av en Postgres-databas och ger dig möjlighet att CRUD data över realtid GraphQL. Det fungerar med både nya och befintliga Postgres databaser. Det har också en access control lager som du kan integrera med alla auth leverantör. I detta inlägg men vi kommer inte att genomföra auth för enkelhetens skull.

Låt oss börja med att driftsätta en instans av Hasura GraphQL Motor till Heroku är gratis nivå som kommer med en färsk Postgres-databas. Gå vidare, gör det; det är gratis och du behöver inte ange dina kreditkortsuppgifter 🙂

När du distribuerar, du kommer att landa upp på Hasura konsol som är admin UI för att hantera dina backend. Observera att den WEBBADRESS du är, är din GraphQL Motor URL. Låt oss börja med att skapa våra tabeller som behövs.

användare

En användare bord kommer att lagra våra användare. För att skapa tabellen, gå till “Data” – fliken högst upp och klicka på “Create Table” – knappen.

Denna tabell har en id-kolumn som är en unik identifierare för varje användare och kolumnen namn som lagrar användarens namn.

styrelsen

Styrelsen tabellen kommer att lagra spel styrelser där all action händer. Vi kommer att snurra upp en ny styrelse för varje spel som startar.

Låt oss titta på kolumnerna i tabellen:

  • id: Ett unikt UUID för varje styrelse som är automatiskt genererad
  • user_1_id: Den user_id för den första användaren. Denna användaren som standard spelar X i spelet
  • user_2_id: Den user_id av andra användare. Denna användaren som standard spelar O.
  • vinnare: Detta är ett textfält som ligger till X eller O när vinnaren har beslutats.
  • turn: det Här är ett textfält som kan vara X eller O och lagrar den aktuella tur. Det börjar med X.

Eftersom user_1_id och user_2_id förvara användar-Id, låt oss lägga en begränsning på styrelsens bord som säkerställer user_1_id och user_2_id att vara närvarande i tabell användaren.

Gå till “Redigera” – fliken i Hasura styrelsen tabell och lägger in de främmande nycklar.

Att lägga till den främmande nyckeln på user_1_id. Vi kommer att behöva lägga till en ny främmande nyckel på user_2_id också.

Nu, baserat på dessa relationer, vi måste skapa en anslutning mellan dessa tabeller så att vi kan fråga användaren information vid sökning i styrelsen.

Gå till “Relationer” – fliken i Hasura och skapa relationer kallas user1 och user2 för user_1_id och user_2_id baserat föreslog relationer.

flytta

Slutligen måste vi flytta en tabell som lagrar rörelser som görs av användare på ett bräde.

Låt oss titta på kolumnerna:

  • id: Det unika id för varje drag som är automatiskt genererad
  • user_id: ID för användaren som gjorde flytten
  • board_id: ID för styrelsen att flytten gjordes
  • position: position där draget gjordes (dvs 1-9)

Eftersom user_id och board_id är främmande nycklar till användare och styrelsens bord, respektive. Vi måste skapa dessa främmande nycklar precis som vi gjorde ovan. Nästa, skapa relationer som användare för främmande nyckel på user_id och styrelsen för främmande nyckel på board_id. Sedan kommer vi gå tillbaka till styrelsens bord är “Relation” – fliken och skapa den föreslagna förhållandet att flytta bordet. Kalla det rör sig.

Vi behöver en egen server

Bortsett från att lagra data i databasen, vi vill också utföra anpassad logik. Logiken? När en användare gör en flytta, flytta måste valideras, som görs före årsskiftet måste slås.

För att kunna göra det, måste vi köra en SQL-transaktion på databasen. Jag har skrivit en GraphQL server som gör exakt det som jag har implementerat på Glitch.

Nu har vi två GraphQL servrar. Men GraphQL spec upprätthåller att det måste vara bara en slutpunkt. För detta ändamål, Hasura stöder fjärrkontroll scheman — dvs du kan ge en extern GraphQL endpoint att Hasura och det kommer att slå samman detta GraphQL server med sig själv och tjäna kombinerade schema under ett och samma slutpunkt. Låt oss lägga denna sed GraphQL server till våra Hasura Motor exempel:

  1. Gaffel GraphQL server.
  2. Lägg till en miljövariabel som är anslutningen till din Postgres-databas. För att göra detta, gå till https://dashboard.heroku.com välj din app, gå till “Inställningar” och avslöja config vars.

Några steg från det:

  1. Kopiera värdet för DATABASE_URL.
  2. Gå till GraphQL server du kluven och klistra in som värde i den .env-fil (POSTGRES_CONNECTION_STRING=<värde>).
  3. Klicka på “Visa Live” – knappen på toppen och kopiera öppnas URL: en.

Vi kommer att lägga till denna URL för att GraphQL motorn genom att lägga till det som en avlägsen schema. Gå till “Remote Scheman” – fliken högst upp och klicka på “Lägg till” alternativet.

Vi är klar med att sätta upp våra backend!

Låt oss arbeta på fronten

Jag kommer inte att gå in på detaljer i front-end genomförandet eftersom y ‘ all skulle välja att genomföra det inom ramen för ditt val. Jag ska gå vidare och ge alla nödvändiga frågor och mutationer som du behöver för att bygga spelet. Med hjälp av dessa med front-end framework med ditt val kommer att tillåta dig att bygga en fullt fungerande multiplayer Tic Tac Toe.

Inställningar

Apollo-Klienten är den som går till biblioteket för klientsidan GraphQL. De har fantastisk abstraktioner för att Reagera, Vue, Vinkel -, iOS, Android etc. Det hjälper dig att spara en hel del boilerplate och DX är slät. Du kanske vill överväga att använda Apollo-klienten över att göra allt från grunden.

Låt oss diskutera de frågor och mutationer som kunden behöver för detta spel.

Infoga användare

I den app som jag byggde, jag genereras ett slumpmässigt lösenord för varje användare och sätts detta namn i databasen när de öppnar appen. Jag har också lagrat namn och genererade ett användar-ID i lokal lagring så att samma användare inte har olika användarnamn. Den mutation som jag använde är:

mutation ($name:String) {
insert_user (
objekt: {
namn: $namn
}
) {
återvänder {
id
}
}
}

Denna mutation skär en post i användarens tabell och returnerar det genererade id: t. Om du observerar mutation noga, det använder $namn. Detta kallas fråga variabel. När du skickar detta mutation till servern tillsammans med variablerna { “name”: “bazooka”}, det GraphQL server skulle ersätta $namn från frågan variabler, vilket i detta fall skulle vara “bazooka.”

Om du önskar, kan du genomföra auth och sätt i denna tabell med användarnamn eller alias.

Ladda alla styrelser

För att ladda alla styrelser, vi kör en GraphQL prenumeration:

prenumeration {
styrelsen (
där: {
_and: {
vinnare: {
_is_null: sanna
},
user_2_id: {
_is_null: sanna
}
}
}
order_by: {
created_at: asc }
) {
id
user1 {
id
namn
}
user_2_id
created_at
vinnare
}
}

Denna prenumeration är en levande fråga som returnerar id, user1 tillsammans med sina id och namn (från förhållandet), user_2_id, vinnare och created_at. Vi har satt ett filter där som hämtar oss bara styrelser utan en giltig vinnare och där user_2_id är noll vilket betyder att styrelsen är öppen för en spelare att gå med. Slutligen har vi för dessa styrelser utgörs av deras created_at tidsstämpel.

Skapa en styrelse

Användare kan skapa styrelser för andra personer att delta. För att göra det, de måste infoga en post i styrelser bordet.

mutation ($user_id: Int) {
insert_board (
objekt: [{
user_1_id: $user_id,
turn: “x”,
}]
) {
återvänder {
id
}
}
}
Gå med i en styrelse

Att gå med i en styrelse, en användare behöver för att uppdatera user_2_id av styrelsen med sina egna user_id. Den mutation som ser ut som:

mutation ($user_id: Int, $board_id: uuid!) {
update_board (
_set: {
user_2_id: $user_id
},
där: {
_and: {
id: {
_eq: $board_id
},
user_2_id: {
_is_null: sanna
},
user_1_id: {
_neq: $user_id
}
}
}
) {
affected_rows
återvänder {
id
}
}
}

I ovanstående GraphQL mutation, vi ställer den user_2_id av en styrelse till en user_id. Vi har även lagt till ytterligare kontroller så att detta lyckas bara om att gå spelaren är inte skaparen och styrelsen inte redan är full. Efter mutation, vi ber för antalet drabbade rader.

I min app, efter att ha gått en styrelse, jag skulle omdirigera användare till /spela?board_id=<board_id>.

Prenumerera till styrelsen

När både användare är i spel, vi behöver uppdateringar i realtid om det rör sig om varje spelare. Så vi måste prenumerera på den givna styrelsen som spelas på och även flyttar (genom relation).

prenumeration($board_id: uuid!) {
styrelse: board_by_pk (id: $board_id) {
id
flyttar (order_by: { id: desc}) {
id
läge
användaren {
id
namn
}
user_id
}
user1 {
id
namn
}
user2 {
id
namn
}
stäng
vinnare
}
}

Ovan frågan samtycker kunden till styrelsen som spelas. När ett nytt drag spelas, kommer kunden vara uppdaterad med det.

Att göra ett drag

Att göra ett drag, som vi kommer att använda den make_move mutation från våra egna GraphQL server.

mutation (
$board_id: String!,
$position: Int!,
$user_id: Int!
) {
make_move (
board_id: $board_id,
position: $ställning,
user_id: $user_id
) {
framgång
}
}

Denna mutation tar en board_id, position och user_id från en fråga variabler. Det validerar flytta, gör flytten och även växla tur. I slutet, returneras om denna transaktion var framgångsrik eller inte.

Tic Tac Whoa!

Och nu har du en fungerande spel Tic Tac Toe! Du kan genomföra alla real-time multiplayer-spel med GraphQL abonnemang med hjälp av de begrepp vi täckt. Låt mig veta om du har några frågor och jag svarar gärna.