DevGang
Авторизоваться

Hasura 101: создание игры в реальном времени с Graphql, Postgres и React 

Мое любимое технологическое открытие 2019 года было Hasura. Это заставляет вставать и работать с полным бэкендом Postgres + GraphQL на одном дыхании  -вы можете кликнуть на некоторые вещи, и тогда у вас будет полностью интерактивная база данных-проводник и редактор, а также конечные точки GraphQL, которые вы можете протестировать с помощью GraphIQL. Я хочу поделиться учебником по созданию игры в реальном времени (с websockets!!!!) на Hasura на основе семинара, который я сделал с ним ранее в этом году.

Мы поговорим о том, что такое GraphQL even, что такое Hasura, как настроить Hasura и базу данных, а затем построить полное приложение React поверх него. Мы создадим приложение для рисования, которое выглядит примерно так:

Вот развернутая версия этого! (Примечание: он использует Heroku free tier, поэтому загрузка может занять несколько секунд)

Что такое GraphQl

Согласно его документации, " GraphQL-это язык запросов для API."Традиционно, с помощью REST API, у вас есть все виды конечных точек, где вы получаете доступ к различным данным или можете изменить данные каким-либо образом. Это может стать довольно громоздким довольно быстро, а также может стать узким местом, если вы работаете с отдельными frontend и backend командами. GraphQL становится действительно полезным, поскольку наши приложения развиваются с течением времени и должны отображать различные данные.

Sacha Grief написал потрясающую аналогию в своей статье "Так что же это такое GraphQL, о которой я постоянно слышу?".

Старая модель REST - это как заказать пиццу, затем получив продукты с доставкой, а затем позвонить в химчистку, чтобы получить свою одежду. Три магазина, три телефонных звонка.GraphQL, с другой стороны, подобен личному помощнику: как только вы дали им адреса всех трех мест, вы можете просто попросить то, что вы хотите (“дайте мне мою химчистку, большую пиццу и две дюжины яиц”), и ждать их возвращения.

GraphQL также является агностиком языка (т. е. вы можете использовать GraphQL с любым языком программирования), и он живет между вашим клиентом и вашими источниками данных, поэтому он очень гибок!

Что такое Hasura

Hasura позволяет вам построить бэкэнд GraphQL с молниеносной скоростью - вы можете просто нажать кнопки и сделать некоторые довольно удивительные вещи.

Hasura:

  • Дает вам мгновенный API GraphQL в реальном времени на новой или существующей базе данных.
  • Он поставляется с приборной панелью, которая поможет вам настроить свой API и базу данных.
  • Вы можете реагировать на изменения в вашей базе данных с помощью веб-хкуков, внешних API или бессерверных функций по запросу.
  • Вы также можете сшить пользовательские API GraphQL и другие источники данных в единый API GraphQL.

Вставай и беги с Hasura

  1. Перейти по этому url-адресу
  2. Войдите в Heroku (создайте учетную запись, если у вас ее нет, не волнуйтесь, это бесплатно!)
  3. Выберите (уникальное) имя для вашего приложения
  4. Нажмите Deploy app
  5. У вас есть развернутый и рабочий экземпляр Hasura!

Настройка базы данных

Мы используем базу данных PostgreSQL для нашего приложения, но Hasura дает нам супер приятный интерфейс для взаимодействия с этой базой данных.

Перейдите в приложение Hasura, которое должно выглядеть примерно так:

Нажмите на вкладку data,а затем кнопку create table рядом с заголовком Schema.  

Мы создадим таблицу pixels в нашей базе данных для хранения координат и цветов каждого пикселя.

Мы также создадим два столбца в этой таблице:id, который будет автоматически увеличенным целым числом, которое обрабатывает Postgres для нас, и color который будет хранить цвет, который должен принимать каждый пиксель.

Кроме того, установите id в качестве первичного ключа.

Вот как должна выглядеть ваша форма!

Затем прокрутите вниз до конца и нажмите кнопку add table!

Теперь у нас есть база данных с нужными нам столбцами 🎉!

Добавление наших исходных данных

В самый первый раз, когда наше приложение загружается, мы хотим, чтобы каждый пиксель был просто белым квадратом, пока люди не начнут их раскрашивать. Чтобы сделать наш будущий код проще, мы будем заполнять базу данных значениями С400, где все имеют цвет white, так как сетка является сеткой 20x20.

Примечание: Вы также можете хранить строки, столбцы и цвета для каждого пикселя, но мне было проще хранить все 400 пикселей и упорядочивать их по идентификатору в пользовательском интерфейсе.

В приборной панели Hasura мы можем запускать SQL-запросы, нажав на ссылку SQL под списком таблиц.

Вы можете добавить свой SQL в текстовое поле, которое появляется, а затем нажмите кнопку Выполнить!

Вот запрос, который я выполнил для заполнения исходной базы данных. Вы можете скопировать и вставить тот же самый, чтобы добавить 400 белых пикселей в базу данных!

Запросы GraphQL

Теперь, когда у нас есть данные, загруженные в нашу базу, мы можем использовать GraphQL для запроса этих данных. Нам больше не нужно делать никаких настроек! Вы можете перейти на GraphIQL панели мониторинга Hasura и поиграть с вашими данными

GraphIQL - это встроенная в браузер IDE для изучения запросов GraphQL. Это поможет вам писать запросы для извлечения и манипулирования данными.

Синтаксис запроса GraphQL очень отличается от SQL-запросов, которые вы можете использовать, - они выглядят более похожими на объекты JavaScript.

Например, чтобы получить все наши данные, наш запрос будет выглядеть следующим образом:

query GetPixels {
  pixels {
    id
    color
  }
}

Во-первых, мы называем наш запрос GetPixels. Затем мы указываем, что хотим получить данные из нашей таблицы pixels. Мы также говорим, что нам нужны данные из столбцов id и color. Если вы опустите один, вы просто получите данные из этого столбца обратно.

Мы также можем изменить запрос, чтобы он всегда упорядочивал пиксели по их идентификаторам:

query GetPixels {
  pixels(order_by: { id: asc }) {
    id
    color
  }
}

Мы также можем написать subscriptions, которые представляют собой запросы, которые также подписываются на изменения в данных через websockets.

Изменение значения слова query на subscription в приведенном выше примере позволит нам извлекать новые данные по мере их обновления.

Кроме того, GraphQL имеет mutations, что позволяет нам обновлять данные.

Например, следующий запрос позволит нам обновить цвет пикселя с учетом его идентификатора:

mutation changePixelColor($id: Int!, $color: String!) {
  update_pixels(where: { id: { _eq: $id } }, _set: { color: $color }) {
    returning {
      color
      id
    }
  }
}

Эта мутация называется changePixelColor, как и функция программирования, мутации (и запросы) могут принимать аргументы. В этом случае он принимает id, что является целым числом, а color строкой. Нам нужно указать таблицу для запроса, в данном случае pixels, что мы можем сделать, набрав update_pixels. Затем мы добавим предложение where - мы только собираемся обновить элемент в базе данных, который соответствует указанному id. Затем мы указываем _set, где говорим, что мы установим цвет нашей строки к указанному.

Затем мы добавляем returning с данными, которые мы хотим отправить обратно в наше приложение, как только наш запрос будет выполнен.

Я настоятельно рекомендую протестировать эти запросы в GraphIQL и использовать их для построения пользовательских запросов!

Вот документация GraphQL, если вы хотите погрузиться глубже!

Интеграция с кодом React

Поскольку этот учебник больше ориентирован на интеграцию Hasura и GraphQL с существующим приложением, мы начнем с некоторого заранее написанного кода реакции. В этом хранилище есть код, из которого мы будем строить. Прямо сейчас, это статическая версия приложения для рисования. Один человек может сделать пиксель-арт, но он не подключается к бэкэнду, поэтому рисунки не сохраняются, и люди не могут рисовать совместно.

Если вы клонируете репозиторий, выполните установку npm install всех его зависимостей. Дайте коду быстрый просмотр, чтобы увидеть, что происходит.

Установка Apollo

Мы будем использовать Apollo, чтобы сделать написание нашего интерфейса GraphQL соединения проще.

В файл Connection.js добавьте следующий код:

import { HttpLink } from "apollo-link-http";
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";

export default new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({
    uri: "your-endpoint.herokuapp.com",
  }),
});

Для этого url используйте конечную точку GraphQL в верхней части вкладки GraphIQL.  

Это настраивает клиент Apollo, чтобы наши запросы GraphQL указывали на нашу конечную точку, которую мы создали.

Нам также нужно добавить несколько строк кода index.js.

import React from "react";
import ReactDOM from "react-dom";
+import { ApolloProvider } from "@apollo/react-hooks";

import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import connection from "./Connection";

ReactDOM.render(
+ <ApolloProvider client={connection}>
    <App />
+ </ApolloProvider>,
  document.getElementById("root")
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

Это дает всему нашему приложению доступ к созданному нами соединению GraphQL. Теперь наши запросы автоматически перейдут в нужное место.

Настройка запроса

Нам нужно попасть в нашу конечную точку GraphQL всякий раз, когда мы заходим в наше приложение, чтобы узнать, какого цвета должен быть каждый пиксель. Мы добавим некоторый код в наш файл App.js, чтобы заставить наше приложение получать данные, которые мы создали вместо статических данных, которые он использует сейчас!

Во-первых, мы импортируем тег шаблона gql. Это позволит нам писать запросы GraphQL в нашем JavaScript-коде. Мы будем использовать наш запрос, который мы написали ранее, чтобы извлечь все пиксели.

const GET_PIXELS = gql`
  query GetPixels {
    pixels(order_by: { id: asc }) {
      color
      id
    }
  }
`;

Затем мы будем использовать крюк useQuery, который предоставляет Apollo, чтобы получить наши данные.

const { loading, error, data } = useQuery(GET_PIXELS);

Этот фрагмент кода будет выполнять наш запрос, когда наш компонент загружается.

Этот хук дает нам три значения: независимо от того, выполняется ли запрос (loading), сообщение об ошибке, если оно существует, и данные, которые возвращаются из запроса.

Прежде чем мы получим наши данные обратно, мы, вероятно, хотим какой-то индикатор загрузки, поэтому мы добавим условие к нашему компоненту, который делает это:

if (loading) {
  return <h2>Loading...</h2>;
}

Мы также изменим наши данные map, чтобы использовать живые данные вместо жестко закодированных пикселей, которые мы в настоящее время создаем в строке 5.  

data.pixels.map((pixel) => (
  <Pixel {...pixel} key={pixel.id} newColor={color} />
));

  В общем и целом, вот что изменилось в нашем App.js:  

import React, { useState } from "react";
+ import { useQuery } from "@apollo/react-hooks";
+ import gql from "graphql-tag";
import Pixel from "./Pixel";
import ColorPicker from "./ColorPicker";

- const pixels = new Array(400).fill("white");

+ const GET_PIXELS = gql`
+   query GetPixels {
+     pixels(order_by: { id: asc }) {
+      color
+      id
+   }
+ }
+`;

function App() {
+ const { loading, error, data } = useQuery(GET_PIXELS);
  const [color, changeColor] = useState("white");

+ if (loading) {
+   return <h2>Loading...<h2/>;
+ }

  return (
    <div className="content">
      <div className="logo">Draw</div>
      <p>Pick a Color</p>
      <ColorPicker changeColor={changeColor} />
      <p>Click a Pixel</p>
      <div className="container">
+       {data.pixels.map(pixel => (
+         <Pixel {...pixel} key={pixel.id} newColor={color} />
+        ))}
      </div>
    </div>
  );
}

export default App;

Установка мутации

Теперь давайте сделаем так, чтобы наш компонент Pixel выполнял нашу мутацию, чтобы изменить цвет пикселя, когда мы нажимаем на пиксель. Это позволит сделать так, чтобы наши изменения сохранялись среди пользователей и сеансов.

Мы снова будем использовать наш тег шаблона gql и поместим нашу мутацию в него.

const UPDATE_COLOR = gql`
  mutation ChangePixelColor($id: Int!, $color: String!) {
    update_pixels(where: { id: { _eq: $id } }, _set: { color: $color }) {
      returning {
        x
        y
        color
        id
      }
    }
  }
`;

У Аполлона также есть хук useMutation, поэтому мы импортируем его и используем.  

const [updatePixelColor] = useMutation(UPDATE_COLOR);

Мы также обновим наш обработчик onClick, чтобы запустить нашу мутацию, когда пользователь нажимает на пиксель.  

onClick={() => {
    changeColor(color);
    updatePixelColor({ variables: { id, color: newColor } });
}}

Вот как наши Pixel.js будут выглядеть, когда мы закончим его конвертировать:  

import React from "react";
+ import gql from "graphql-tag";
+ import { useMutation } from "@apollo/react-hooks";

+ const UPDATE_COLOR = gql`
+ mutation ChangePixelColor($id: Int!, $color: String!) {
+   update_pixels(where: { id: { _eq: $id } }, _set: { color: $color }) {
+     returning {
+       color
+       id
+     }
+   }
+ }
+ `;

const Pixel = ({ id, color, newColor }) => {
+ const [updatePixelColor] = useMutation(UPDATE_COLOR);

  return (
    <span
      className="pixel"
      onClick={() => {
         changeColor(color);
+        updatePixelColor({ variables: { id, color: newColor } });
      }}
      style={{ backgroundColor: color }}
    ></span>
  );
};

export default Pixel;

Теперь наше приложение подключается к нашей конечной точке GraphQL и извлекает правильные данные. Вот код решения, если вы хотите увидеть его в полном объеме!

Создание в реальном времени

Прямо сейчас наше приложение извлекает из нашей конечной точки GraphQL при загрузке страницы, но оно не обновляется в режиме реального времени, когда другой пользователь нажимает на пиксель. Мы хотим, чтобы наши пользователи могли рисовать в режиме реального времени со своими друзьями. Вы можете попробовать это, открыв развернутое приложение в двух вкладках - если вы обновите пиксель в одной вкладке, другая тоже должна обновиться.

Нам просто нужно обновить наш App.js, чтобы использовать подписку GraphQL вместо запроса.

Мы будем использовать хук Apollo useSubscription вместо useQuery и изменим слово query в нашем запросе на subscription. Магия🧙 🏻 ♂️!

Вот разница, показывающая, что изменилось! (Примечание: большая часть файла опущена, так как не сильно изменилась)

import React, { useState } from "react";
+ import { useSubscription } from "@apollo/react-hooks";
import gql from "graphql-tag";

import Pixel from "./Pixel";
import ColorPicker from "./ColorPicker";

const pixels = new Array(400).fill("white");

const GET_PIXELS = gql`
+ subscription GetPixels {
    pixels(order_by: { id: asc }) {
      color
      id
    }
  }
`;

function App() {
  const [color, changeColor] = useState("white");
+ const { loading, error, data } = useSubscription(GET_PIXELS);

...

Вот полный код с подписками!

Дальнейшие действия

  • Вы можете добавить некоторые пользовательские бэкенд логики игры с действиями Hasura.
  • Если вы хотите узнать больше о GraphQL, вот удивительный курс .
  • Если вы хотите погрузиться глубже в создание бэкенда с Hasura вот еще один курс.
  • Есть еще тонна учебников на сайте Hasura.

Вывод

Мне так нравится создавать приложения с помощью этого стека - он позволяет мне сосредоточиться исключительно на интерфейсном коде для простых приложений. Я могу создать приложение, которое позволяет двум пользователям взаимодействовать в реальном времени с очень небольшим трением. Традиционно, написание полного бэкенда со слоем GraphQL - это довольно обширный процесс, который требует много обслуживания. С Hasura, мы можем сделать это с помощью нескольких щелчков мыши. Это мой новый go-to stack для построения быстрых приложений.

Кроме того, вот видео версия снова, если вы хотите посмотреть!

Источник:

#React #GraphQL
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

Поделитесь своим опытом, расскажите о новом инструменте, библиотеке или фреймворке. Для этого не обязательно становится постоянным автором.

Попробовать

Напиши статью и выиграй годовую подписку на Яндекс плюс или лицензию от Jet Brains

Участвовать