Как создать приложение реального времени с подписками GraphQL на Postgres
Опубликовано: 2022-03-10В этой статье мы рассмотрим проблемы, связанные с созданием приложений реального времени, и то, как новые инструменты решают их с помощью элегантных решений, о которых легко рассуждать. Для этого мы создадим приложение для опроса в реальном времени (например, опрос в Твиттере с общей статистикой в реальном времени), просто используя Postgres, GraphQL, React и без внутреннего кода!
Основное внимание будет уделено настройке серверной части (развертывание готовых к использованию инструментов, моделирование схем) и аспектам интеграции внешнего интерфейса с GraphQL, а не UI/UX внешнего интерфейса (некоторое знание ReactJS поможет). В учебном разделе будет использоваться метод рисования по номерам, поэтому мы просто клонируем репозиторий GitHub для моделирования схемы и пользовательского интерфейса и настраиваем его, вместо того, чтобы создавать все приложение с нуля.
Всё о GraphQL
Знаете ли вы все, что вам нужно знать о GraphQL? Если у вас есть сомнения, Эрик Баер предоставит вам подробное руководство о его происхождении, недостатках и основах работы с ним. Читать статью по теме →
Прежде чем вы продолжите читать эту статью, я хотел бы отметить, что полезно знать следующие технологии (или заменители):
- РеактJS
Его можно заменить любой внешней средой, Android или IOS, следуя документации по клиентской библиотеке. - Постгрес
Вы можете работать с другими базами данных, но с другими инструментами, принципы, изложенные в этом посте, все равно будут применяться.
Вы также можете очень легко адаптировать этот обучающий контекст для других приложений реального времени.
Как показано в сопутствующей полезной нагрузке GraphQL внизу, есть три основные функции, которые нам необходимо реализовать:
- Получите вопрос опроса и список вариантов (вверху слева).
- Разрешить пользователю голосовать за данный вопрос опроса (кнопка «Проголосовать»).
- Получить результаты опроса в режиме реального времени и отобразить их в виде гистограммы (вверху справа; мы можем замаскировать эту функцию, чтобы получить список пользователей, которые в настоящее время находятся в сети, поскольку это точная копия этого варианта использования).
Проблемы с созданием приложений реального времени
Создание приложений реального времени (особенно в качестве разработчика внешнего интерфейса или того, кто недавно перешел на работу с полным стеком) — сложная инженерная задача, которую необходимо решить. Как правило, современные приложения реального времени работают следующим образом (в контексте нашего примерного приложения):
- Внешний интерфейс обновляет базу данных некоторой информацией; Голос пользователя отправляется на серверную часть, т.е. опрос/опция и информация о пользователе (
user_id
,option_id
). - Первое обновление запускает другую службу, которая агрегирует данные опроса для обработки выходных данных, которые передаются обратно в приложение в режиме реального времени (каждый раз, когда кем-либо подается новый голос; если это сделано эффективно, обрабатываются только обновленные данные опроса и обновляются только те клиенты, которые подписались на этот опрос):
- Данные голосования сначала обрабатываются сервисом
register_vote
(предположим, что здесь происходит некоторая проверка), который запускает сервисpoll_results
. - Сводные данные опроса в режиме реального времени передаются службой
poll_results
во внешний интерфейс для отображения общей статистики.
- Данные голосования сначала обрабатываются сервисом
Эта модель основана на традиционном подходе к созданию API и, следовательно, имеет схожие проблемы:
- Любой из последовательных шагов может пойти не так, что приведет к зависанию UX и повлияет на другие независимые операции.
- Требует больших усилий на уровне API, поскольку это единая точка контакта для внешнего приложения, которое взаимодействует с несколькими службами. Также необходимо реализовать API реального времени на основе веб-сокетов — для этого не существует универсального стандарта, и поэтому в инструментах предусмотрена ограниченная поддержка автоматизации.
- Фронтенд-приложение требуется для добавления необходимой сантехники для использования API реального времени, а также, возможно, должно решить проблему согласованности данных, обычно встречающуюся в приложениях реального времени (менее важная в нашем выбранном примере, но критическая для упорядочения сообщений в реальном времени). приложение для чата).
- Многие реализации прибегают к использованию дополнительных нереляционных баз данных на стороне сервера (Firebase и т. д.) для упрощения поддержки API в реальном времени.
Давайте посмотрим, как GraphQL и связанные с ним инструменты решают эти проблемы.
Что такое GraphQL?
GraphQL — это спецификация языка запросов для API и среда выполнения на стороне сервера для выполнения запросов. Эта спецификация была разработана Facebook для ускорения разработки приложений и предоставления стандартизированного, независимого от базы данных формата доступа к данным. Любой сервер GraphQL, соответствующий спецификации, должен поддерживать следующее:
- Запросы на чтение
Тип запроса для запроса вложенных данных из источника данных (который может быть либо одной, либо комбинацией базы данных, REST API или другой схемы/сервера GraphQL). - Мутации для записи
Тип запроса для записи/передачи данных в вышеупомянутые источники данных. - Подписки на live-запросы
Тип запроса для клиентов на подписку на обновления в реальном времени.
GraphQL также использует типизированную схему. В экосистеме есть множество инструментов, которые помогают выявлять ошибки во время разработки/компиляции, что приводит к меньшему количеству ошибок во время выполнения.
Вот почему GraphQL отлично подходит для приложений реального времени:
- Live-запросы (подписки) являются неявной частью спецификации GraphQL. Любая система GraphQL должна иметь встроенные возможности API в реальном времени.
- Стандартная спецификация для запросов в реальном времени объединила усилия сообщества в отношении инструментов на стороне клиента, что привело к очень интуитивно понятному способу интеграции с API-интерфейсами GraphQL.
GraphQL и сочетание инструментов с открытым исходным кодом для событий базы данных и бессерверных/облачных функций предлагают отличную основу для создания облачных приложений с асинхронной бизнес-логикой и функциями реального времени, которые легко создавать и которыми легко управлять. Эта новая парадигма также дает отличный опыт для пользователей и разработчиков.
В оставшейся части этой статьи я буду использовать инструменты с открытым исходным кодом для создания приложения на основе этой архитектурной схемы:
Создание приложения для опроса/голосования в реальном времени
После этого введения в GraphQL давайте вернемся к созданию приложения для опроса, как описано в первом разделе.
Три функции (или выделенные истории) были выбраны для демонстрации различных типов запросов GraphQL, которые будет делать наше приложение:
- Запрос
Получить вопрос опроса и его варианты. - Мутация
Позвольте пользователю проголосовать. - Подписка
Отображение панели мониторинга в реальном времени для результатов опроса.
Предпосылки
- Учетная запись Heroku (используйте бесплатный уровень, кредитная карта не требуется)
Чтобы развернуть серверную часть GraphQL (см. следующий пункт ниже) и экземпляр Postgres. - Hasura GraphQL Engine (бесплатный, с открытым исходным кодом) Готовый к использованию сервер GraphQL на Postgres.
- Клиент Apollo (бесплатный SDK с открытым исходным кодом)
Для простой интеграции клиентских приложений с сервером GraphQL. - npm (бесплатный менеджер пакетов с открытым исходным кодом)
Чтобы запустить наше приложение React.
Развертывание базы данных и серверной части GraphQL
Мы развернем по одному экземпляру Postgres и GraphQL Engine на бесплатном уровне Heroku. Мы можем использовать изящную кнопку Heroku, чтобы сделать это одним щелчком мыши.
Примечание. Вы также можете перейти по этой ссылке или выполнить поиск документации по развертыванию Hasura GraphQL для Heroku (или других платформ).
Вам не потребуется никаких дополнительных настроек, и вы можете просто нажать кнопку «Развернуть приложение». После завершения развертывания запишите URL-адрес приложения:
<app-name>.herokuapp.com
Например, на скриншоте выше это будет:
hge-realtime-app-tutorial.herokuapp.com
Что мы сделали до сих пор, так это развернули экземпляр Postgres (как дополнение на языке Heroku) и экземпляр GraphQL Engine, который настроен на использование этого экземпляра Postgres. В результате у нас теперь есть готовый к использованию GraphQL API, но, поскольку у нас нет таблиц или данных в нашей базе данных, это пока бесполезно. Итак, давайте рассмотрим это немедленно.
Моделирование схемы базы данных
На следующей схеме показана простая схема реляционной базы данных для нашего приложения для опроса:
Как видите, это простая нормализованная схема, в которой используются ограничения внешнего ключа. Именно эти ограничения интерпретируются механизмом GraphQL как отношения 1:1 или 1:множество (например poll:options
представляет собой отношение 1:множество, поскольку каждый опрос будет иметь более 1 параметра, которые связаны ограничением внешнего ключа между столбец id
таблицы poll
и столбец poll_id
в таблице option
). Связанные данные могут быть смоделированы в виде графика и, таким образом, могут использоваться в GraphQL API. Это именно то, что делает GraphQL Engine.
Основываясь на вышеизложенном, нам нужно будет создать следующие таблицы и ограничения для моделирования нашей схемы:
-
Poll
Таблица для записи вопроса опроса. -
Option
Варианты для каждого опроса. -
Vote
Для записи голоса пользователя. - Ограничение внешнего ключа между следующими полями (
table : column
):-
option : poll_id → poll : id
-
vote : poll_id → poll : id
-
vote : created_by_user_id → user : id
-
Теперь, когда у нас есть проект схемы, давайте реализуем его в нашей базе данных Postgres. Чтобы мгновенно вывести эту схему, вот что мы сделаем:
- Загрузите интерфейс командной строки GraphQL Engine.
- Клонируйте это репо:
$ git clone clone https://github.com/hasura/graphql-engine $ cd graphql-engine/community/examples/realtime-poll
- Перейдите в
hasura/
и отредактируйтеconfig.yaml
:endpoint: https://<app-name>.herokuapp.com
- Примените миграции с помощью CLI из каталога проекта (который вы только что загрузили путем клонирования):
$ hasura migrate apply
Это все для бэкэнда. Теперь вы можете открыть консоль GraphQL Engine и проверить наличие всех таблиц (консоль доступна по адресу https://<app-name>.herokuapp.com/console
).
Примечание. Вы также можете использовать консоль для реализации схемы, создав отдельные таблицы, а затем добавив ограничения с помощью пользовательского интерфейса. Использование встроенной поддержки миграций в GraphQL Engine — это просто удобный вариант, который был доступен, потому что в нашем репозитории примеров есть миграции для создания необходимых таблиц и настройки отношений/ограничений (это также настоятельно рекомендуется, независимо от того, создаете ли вы хобби). проект или готовое к производству приложение).
Интеграция внешнего приложения React с бэкэндом GraphQL
Внешний интерфейс в этом руководстве представляет собой простое приложение, которое показывает вопрос опроса, возможность голосования и сводные результаты опроса в одном месте. Как я упоминал ранее, сначала мы сосредоточимся на запуске этого приложения, чтобы вы получили мгновенное удовлетворение от использования нашего недавно развернутого API GraphQL. Посмотрите, как концепции GraphQL, которые мы рассмотрели ранее в этой статье, влияют на различные варианты использования такого приложения. , а затем изучите внутреннюю работу интеграции GraphQL.
ПРИМЕЧАНИЕ. Если вы новичок в ReactJS, вы можете ознакомиться с некоторыми из этих статей. Мы не будем вдаваться в детали React-части приложения, а вместо этого сосредоточимся больше на GraphQL-аспектах приложения. Вы можете обратиться к исходному коду в репозитории для получения подробной информации о том, как было построено приложение React .
Настройка внешнего интерфейса
- В репозитории, клонированном в предыдущем разделе, отредактируйте
HASURA_GRAPHQL_ENGINE_HOSTNAME
в файле src/apollo.js (внутри папки/community/examples/realtime-poll
) и укажите URL-адрес приложения Heroku, указанный выше:export const HASURA_GRAPHQL_ENGINE_HOSTNAME = 'random-string-123.herokuapp.com';
- Перейдите в корень репозитория/папки приложения (
/realtime-poll/
) и используйте npm для установки необходимых модулей, а затем запустите приложение:$ npm install $ npm start
Теперь вы сможете поиграть с приложением. Идите вперед и голосуйте столько раз, сколько хотите, вы заметите, что результаты меняются в режиме реального времени. На самом деле, если вы настроите другой экземпляр этого пользовательского интерфейса и укажете его на тот же бэкэнд, вы сможете увидеть результаты, агрегированные по всем экземплярам.
Итак, как это приложение использует GraphQL? Читать дальше.
За кулисами: GraphQL
В этом разделе мы рассмотрим функции GraphQL, лежащие в основе приложения, а затем продемонстрируем простоту интеграции в следующем разделе.
Компонент опроса и график сводных результатов
Компонент опроса в левом верхнем углу, который выбирает опрос со всеми его параметрами и фиксирует голос пользователя в базе данных. Обе эти операции выполняются с использованием GraphQL API. Для получения сведений об опросе мы делаем запрос (помните это из введения в GraphQL?):
query { poll { id question options { id text } } }
Используя компонент Mutation из react-apollo
, мы можем подключить мутацию к HTML-форме, чтобы мутация выполнялась с использованием переменных optionId
и userId
при отправке формы:
mutation vote($optionId: uuid!, $userId: uuid!) { insert_vote(objects: [{option_id: $optionId, created_by_user_id: $userId}]) { returning { id } } }
Чтобы показать результаты опроса, нам нужно получить количество голосов за вариант из данных в таблице голосования. Мы можем создать представление Postgres и отслеживать его с помощью GraphQL Engine, чтобы сделать эти производные данные доступными через GraphQL.
CREATE VIEW poll_results AS SELECT poll.id AS poll_id, o.option_id, count(*) AS votes FROM (( SELECT vote.option_id, option.poll_id, option.text FROM ( vote LEFT JOIN public.option ON ((option.id = vote.option_id)))) o LEFT JOIN poll ON ((poll.id = o.poll_id))) GROUP BY poll.question, o.option_id, poll.id;
Представление poll_results
объединяет данные из таблиц vote
и poll
, чтобы обеспечить совокупный подсчет количества голосов по каждому варианту.
Используя GraphQL Subscriptions для этого представления, react-google-charts и компонент подписки из react-apollo
, мы можем подключить реактивную диаграмму, которая обновляется в реальном времени, когда происходит новое голосование от любого клиента.
subscription getResult($pollId: uuid!) { poll_results(where: {poll_id: {_eq: $pollId}}) { option { id text } votes } }
Интеграция API GraphQL
Как я упоминал ранее, я использовал Apollo Client, SDK с открытым исходным кодом, для интеграции приложения ReactJS с серверной частью GraphQL. Клиент Apollo аналогичен любой клиентской библиотеке HTTP, такой как запросы для python, стандартный модуль http для JavaScript и так далее. Он инкапсулирует детали создания HTTP-запроса (в данном случае POST-запроса). Он использует конфигурацию (указанную в src/apollo.js
) для выполнения запросов/мутаций/подписки (указанных в src/GraphQL.jsx с возможностью использования переменных, которые можно динамически заменять в коде JavaScript вашего приложения REACT) для конечная точка GraphQL. Он также использует типизированную схему конечной точки GraphQL для обеспечения проверки времени компиляции/разработки для вышеупомянутых запросов. Давайте посмотрим, насколько просто клиентскому приложению сделать запрос в режиме реального времени (подписку) к GraphQL API.
Настройка SDK
Клиентский SDK Apollo должен быть направлен на сервер GraphQL, чтобы он мог автоматически обрабатывать шаблонный код, обычно необходимый для такой интеграции. Итак, это именно то, что мы сделали, когда изменили src/apollo.js при настройке внешнего приложения.
Создание запроса на подписку GraphQL (Live-Query)
Определите подписку, которую мы рассмотрели в предыдущем разделе, в файле src/GraphQL.jsx :
const SUBSCRIPTION_RESULT = ` subscription getResult($pollId: uuid!) { poll_results ( order_by: option_id_desc, where: { poll_id: {_eq: $pollId} } ) { option_id option { id text } votes } }`;
Мы будем использовать это определение для подключения нашего компонента React:
export const Result = (pollId) => ( <Subscription subscription={gql`${SUBSCRIPTION_RESULT}`} variables={pollId}> {({ loading, error, data }) => { if (loading) return
Загрузка...</p>; если (ошибка) вернуть
Ошибка:</p>; вернуть ( <дел> <дел> {RenderChart(данные)} </div> </div> ); }} </подписка> )
Здесь следует отметить, что приведенная выше подписка также могла быть запросом. Простая замена одного ключевого слова на другое дает нам «живой запрос», и это все, что требуется от Apollo Client SDK, чтобы связать этот API в реальном времени с вашим приложением. Каждый раз, когда появляется новый набор данных из нашего живого запроса, SDK запускает повторную визуализацию нашей диаграммы с этими обновленными данными (используя renderChart(data)
). Вот и все. Это действительно настолько просто!
Последние мысли
В три простых шага (создание бэкенда GraphQL, моделирование схемы приложения и интеграция внешнего интерфейса с GraphQL API) вы можете быстро подключить полнофункциональное приложение реального времени, не увязая в ненужных деталях, таких как настройка соединение через веб-сокет. Именно в этом заключается сила инструментов сообщества, поддерживающих такую абстракцию, как GraphQL.
Если вы нашли это интересным и хотите продолжить изучение GraphQL для своего следующего побочного проекта или производственного приложения, вот некоторые факторы, которые вы можете использовать для создания своей цепочки инструментов GraphQL:
- Производительность и масштабируемость
GraphQL предназначен для непосредственного использования фронтенд-приложениями (это не лучше, чем ORM в бэкенде; от этого исходят реальные преимущества производительности). Таким образом, ваш инструментарий должен уметь эффективно использовать соединения с базой данных и легко масштабироваться. - Безопасность
Из вышеизложенного следует, что для авторизации доступа к данным необходима зрелая ролевая система управления доступом. - Автоматизация
Если вы новичок в экосистеме GraphQL, написание схемы GraphQL от руки и реализация сервера GraphQL могут показаться сложной задачей. Максимизируйте автоматизацию своих инструментов, чтобы вы могли сосредоточиться на важных вещах, таких как создание ориентированных на пользователя функций внешнего интерфейса. - Архитектура Какими бы тривиальными ни казались приведенные выше усилия, внутренняя архитектура приложения производственного уровня может включать расширенные концепции GraphQL, такие как сшивание схемы и т. д. Более того, возможность легко генерировать/использовать API в реальном времени открывает возможность создания асинхронных, реактивных приложения, которые являются устойчивыми и по своей природе масштабируемыми. Поэтому очень важно оценить, как инструменты GraphQL могут упростить вашу архитектуру.
Связанные ресурсы
- Вы можете проверить живую версию приложения здесь.
- Полный исходный код доступен на GitHub.
- Если вы хотите изучить схему базы данных и выполнить тестовые запросы GraphQL, вы можете сделать это здесь.