Cómo crear una aplicación en tiempo real con suscripciones de GraphQL en Postgres
Publicado: 2022-03-10En este artículo, veremos los desafíos involucrados en la creación de aplicaciones en tiempo real y cómo las herramientas emergentes los abordan con soluciones elegantes que son fáciles de razonar. Para hacer esto, crearemos una aplicación de encuestas en tiempo real (como una encuesta de Twitter con estadísticas generales en tiempo real) simplemente usando Postgres, GraphQL, React y ¡sin código de back-end!
El enfoque principal estará en configurar el backend (implementación de herramientas listas para usar, modelado de esquemas) y aspectos de integración frontend con GraphQL y menos en UI/UX del frontend (algo de conocimiento de ReactJS ayudará). La sección del tutorial adoptará un enfoque de pintura por números, por lo que simplemente clonaremos un repositorio de GitHub para el modelado del esquema y la interfaz de usuario y lo modificaremos, en lugar de crear toda la aplicación desde cero.
Todas las cosas GraphQL
¿Sabes todo lo que necesitas saber sobre GraphQL? Si tienes dudas, Eric Baer te resuelve con una guía detallada sobre sus orígenes, sus inconvenientes y los conceptos básicos de cómo trabajar con él. Leer un artículo relacionado →
Antes de continuar leyendo este artículo, me gustaría mencionar que un conocimiento práctico de las siguientes tecnologías (o sustitutos) es beneficioso:
- ReaccionarJS
Esto se puede reemplazar con cualquier marco de interfaz, Android o IOS siguiendo la documentación de la biblioteca del cliente. - postgres
Puede trabajar con otras bases de datos, pero con diferentes herramientas, los principios descritos en esta publicación seguirán aplicándose.
También puede adaptar este contexto tutorial para otras aplicaciones en tiempo real muy fácilmente.
Como se ilustra en la carga útil de GraphQL adjunta en la parte inferior, hay tres características principales que debemos implementar:
- Obtenga la pregunta de la encuesta y una lista de opciones (arriba a la izquierda).
- Permitir que un usuario vote por una pregunta de encuesta determinada (el botón "Votar").
- Obtenga los resultados de la encuesta en tiempo real y muéstrelos en un gráfico de barras (arriba a la derecha; podemos pasar por alto la función para obtener una lista de los usuarios actualmente en línea, ya que es una réplica exacta de este caso de uso).
Desafíos con la creación de aplicaciones en tiempo real
La creación de aplicaciones en tiempo real (especialmente como desarrollador front-end o alguien que recientemente hizo la transición para convertirse en un desarrollador fullstack) es un problema de ingeniería difícil de resolver. En general, así es como funcionan las aplicaciones contemporáneas en tiempo real (en el contexto de nuestra aplicación de ejemplo):
- La interfaz actualiza una base de datos con cierta información; El voto de un usuario se envía al backend, es decir, encuesta/opción e información del usuario (
user_id
,option_id
). - La primera actualización activa otro servicio que agrega los datos de la encuesta para generar un resultado que se transmite a la aplicación en tiempo real (cada vez que alguien emite un nuevo voto; si esto se hace de manera eficiente, solo se procesan los datos de la encuesta actualizada y solo se actualizan aquellos clientes que se han suscrito a esta encuesta):
- Los datos de voto primero son procesados por un servicio
register_vote
(supongamos que aquí ocurre alguna validación) que activa un serviciopoll_results
. - El servicio
poll_results
transmite los datos agregados de la encuesta en tiempo real a la interfaz para mostrar las estadísticas generales.
- Los datos de voto primero son procesados por un servicio
Este modelo se deriva de un enfoque tradicional de creación de API y, en consecuencia, tiene problemas similares:
- Cualquiera de los pasos secuenciales podría salir mal, dejando el UX colgando y afectando otras operaciones independientes.
- Requiere mucho esfuerzo en la capa API, ya que es un único punto de contacto para la aplicación frontend, que interactúa con múltiples servicios. También necesita implementar una API en tiempo real basada en websockets; no existe un estándar universal para esto y, por lo tanto, ve un soporte limitado para la automatización en las herramientas.
- Se requiere que la aplicación frontend agregue las conexiones necesarias para consumir la API en tiempo real y también puede tener que resolver el problema de consistencia de datos que normalmente se ve en las aplicaciones en tiempo real (menos importante en nuestro ejemplo elegido, pero fundamental para ordenar mensajes en un entorno real). -aplicación de chat de tiempo).
- Muchas implementaciones recurren al uso de bases de datos no relacionales adicionales en el lado del servidor (Firebase, etc.) para facilitar el soporte de API en tiempo real.
Echemos un vistazo a cómo GraphQL y las herramientas asociadas abordan estos desafíos.
¿Qué es GraphQL?
GraphQL es una especificación para un lenguaje de consulta para API y un tiempo de ejecución del lado del servidor para ejecutar consultas. Esta especificación fue desarrollada por Facebook para acelerar el desarrollo de aplicaciones y proporcionar un formato de acceso a datos estandarizado e independiente de la base de datos. Cualquier servidor GraphQL que cumpla con las especificaciones debe ser compatible con lo siguiente:
- Consultas de lecturas
Un tipo de solicitud para solicitar datos anidados de una fuente de datos (que puede ser una base de datos o una combinación de una base de datos, una API REST u otro esquema/servidor GraphQL). - Mutaciones para escrituras
Un tipo de solicitud para escribir/transmitir datos en las fuentes de datos antes mencionadas. - Suscripciones para consultas en vivo
Un tipo de solicitud para que los clientes se suscriban a actualizaciones en tiempo real.
GraphQL también usa un esquema escrito. El ecosistema tiene muchas herramientas que lo ayudan a identificar errores en el momento de desarrollo/compilación, lo que se traduce en menos errores de tiempo de ejecución.
He aquí por qué GraphQL es ideal para aplicaciones en tiempo real:
- Las consultas en vivo (suscripciones) son una parte implícita de la especificación GraphQL. Cualquier sistema GraphQL debe tener capacidades nativas de API en tiempo real.
- Una especificación estándar para consultas en tiempo real ha consolidado los esfuerzos de la comunidad en torno a las herramientas del lado del cliente, lo que da como resultado una forma muy intuitiva de integración con las API de GraphQL.
GraphQL y una combinación de herramientas de código abierto para eventos de base de datos y funciones sin servidor/en la nube ofrecen un excelente sustrato para crear aplicaciones nativas de la nube con lógica empresarial asíncrona y funciones en tiempo real que son fáciles de crear y administrar. Este nuevo paradigma también da como resultado una excelente experiencia para el usuario y el desarrollador.
En el resto de este artículo, usaré herramientas de código abierto para crear una aplicación basada en este diagrama de arquitectura:
Creación de una aplicación de votación/encuesta en tiempo real
Con esa introducción a GraphQL, volvamos a crear la aplicación de sondeo como se describe en la primera sección.
Las tres funciones (o historias destacadas) se eligieron para demostrar los diferentes tipos de solicitudes de GraphQL que realizará nuestra aplicación:
- Consulta
Obtenga la pregunta de la encuesta y sus opciones. - Mutación
Permita que un usuario emita un voto. - Suscripción
Muestre un tablero en tiempo real para los resultados de la encuesta.
requisitos previos
- Una cuenta de Heroku (use el nivel gratuito, no se requiere tarjeta de crédito)
Para implementar un backend de GraphQL (consulte el siguiente punto a continuación) y una instancia de Postgres. - Motor Hasura GraphQL (gratis, de código abierto) Un servidor GraphQL listo para usar en Postgres.
- Apollo Client (SDK gratuito y de código abierto)
Para integrar fácilmente aplicaciones de clientes con un servidor GraphQL. - npm (administrador de paquetes gratuito y de código abierto)
Para ejecutar nuestra aplicación React.
Implementación de la base de datos y un backend de GraphQL
Implementaremos una instancia de Postgres y GraphQL Engine en el nivel gratuito de Heroku. Podemos usar un ingenioso botón Heroku para hacer esto con un solo clic.
Nota: También puede seguir este enlace o buscar la documentación Implementación de Hasura GraphQL para Heroku (u otras plataformas).
No necesitará ninguna configuración adicional, y simplemente puede hacer clic en el botón "Implementar aplicación". Una vez completada la implementación, tome nota de la URL de la aplicación:
<app-name>.herokuapp.com
Por ejemplo, en la captura de pantalla anterior, sería:
hge-realtime-app-tutorial.herokuapp.com
Lo que hemos hecho hasta ahora es implementar una instancia de Postgres (como un complemento en el lenguaje de Heroku) y una instancia de GraphQL Engine que está configurada para usar esta instancia de Postgres. Como resultado de ello, ahora tenemos una API de GraphQL lista para usar, pero, dado que no tenemos tablas ni datos en nuestra base de datos, todavía no es útil. Entonces, abordemos esto de inmediato.
Modelado del esquema de la base de datos
El siguiente diagrama de esquema captura un esquema de base de datos relacional simple para nuestra aplicación de encuesta:
Como puede ver, el esquema es simple y normalizado que aprovecha las restricciones de clave externa. Son estas restricciones las que el motor GraphQL interpreta como relaciones 1:1 o 1:muchas (por ejemplo poll:options
es una relación 1:muchos, ya que cada sondeo tendrá más de 1 opción que están vinculadas por la restricción de clave externa entre el columna id
de la tabla de poll
y la columna poll_id
en la tabla de option
). Los datos relacionados se pueden modelar como un gráfico y, por lo tanto, pueden impulsar una API GraphQL. Esto es precisamente lo que hace GraphQL Engine.
En base a lo anterior, tendremos que crear las siguientes tablas y restricciones para modelar nuestro esquema:
-
Poll
Una tabla para capturar la pregunta de la encuesta. -
Option
Opciones para cada encuesta. -
Vote
Para registrar el voto de un usuario. - Restricción de clave externa entre los siguientes campos (
table : column
):-
option : poll_id → poll : id
-
vote : poll_id → poll : id
-
vote : created_by_user_id → user : id
-
Ahora que tenemos nuestro diseño de esquema, implementémoslo en nuestra base de datos de Postgres. Para mostrar instantáneamente este esquema, esto es lo que haremos:
- Descargue la CLI del motor GraphQL.
- Clonar este repositorio:
$ git clone clone https://github.com/hasura/graphql-engine $ cd graphql-engine/community/examples/realtime-poll
- Ve a
hasura/
y editaconfig.yaml
:endpoint: https://<app-name>.herokuapp.com
- Aplique las migraciones usando la CLI, desde dentro del directorio del proyecto (que acaba de descargar mediante la clonación):
$ hasura migrate apply
Eso es todo para el backend. Ahora puede abrir la consola de GraphQL Engine y verificar que todas las tablas estén presentes (la consola está disponible en https://<app-name>.herokuapp.com/console
).
Nota: También podría haber usado la consola para implementar el esquema creando tablas individuales y luego agregando restricciones usando una interfaz de usuario. Usar el soporte incorporado para migraciones en GraphQL Engine es solo una opción conveniente que estaba disponible porque nuestro repositorio de muestra tiene migraciones para mostrar las tablas requeridas y configurar relaciones/restricciones (esto también es muy recomendable independientemente de si está creando un pasatiempo). proyecto o una aplicación lista para producción).
Integración de la aplicación Frontend React con el backend GraphQL
La interfaz de este tutorial es una aplicación simple que muestra la pregunta de la encuesta, la opción de votar y los resultados agregados de la encuesta en un solo lugar. Como mencioné anteriormente, primero nos enfocaremos en ejecutar esta aplicación para que obtenga la gratificación instantánea de usar nuestra API GraphQL implementada recientemente, vea cómo los conceptos de GraphQL que vimos anteriormente en este artículo potencian los diferentes casos de uso de dicha aplicación. y luego explore cómo funciona la integración de GraphQL bajo el capó.
NOTA: Si es nuevo en ReactJS, es posible que desee consultar algunos de estos artículos. No entraremos en los detalles de la parte React de la aplicación y, en cambio, nos centraremos más en los aspectos GraphQL de la aplicación. Puede consultar el código fuente en el repositorio para obtener detalles sobre cómo se ha creado la aplicación React .
Configuración de la aplicación frontend
- En el repositorio clonado en la sección anterior, edite
HASURA_GRAPHQL_ENGINE_HOSTNAME
en el archivo src/apollo.js (dentro de la carpeta/community/examples/realtime-poll
) y configúrelo en la URL de la aplicación Heroku desde arriba:export const HASURA_GRAPHQL_ENGINE_HOSTNAME = 'random-string-123.herokuapp.com';
- Vaya a la raíz del repositorio/carpeta de aplicaciones (
/realtime-poll/
) y use npm para instalar los módulos necesarios y luego ejecute la aplicación:$ npm install $ npm start
Deberías poder jugar con la aplicación ahora. Anímate a votar tantas veces como quieras, notarás que los resultados cambian en tiempo real. De hecho, si configura otra instancia de esta interfaz de usuario y la apunta al mismo backend, podrá ver los resultados agregados en todas las instancias.
Entonces, ¿cómo usa esta aplicación GraphQL? sigue leyendo
Detrás de escena: GraphQL
En esta sección, exploraremos las características de GraphQL que impulsan la aplicación, seguido de una demostración de la facilidad de integración en la siguiente.
El componente de encuesta y el gráfico de resultados agregados
El componente de encuesta en la parte superior izquierda que obtiene una encuesta con todas sus opciones y captura el voto de un usuario en la base de datos. Ambas operaciones se realizan mediante la API de GraphQL. Para obtener los detalles de una encuesta, hacemos una consulta (¿recuerdas esto de la introducción de GraphQL?):
query { poll { id question options { id text } } }
Usando el componente de mutación de react-apollo
, podemos conectar la mutación a un formulario HTML de modo que la mutación se ejecute usando las variables optionId
y userId
cuando se envía el formulario:
mutation vote($optionId: uuid!, $userId: uuid!) { insert_vote(objects: [{option_id: $optionId, created_by_user_id: $userId}]) { returning { id } } }
Para mostrar los resultados de la encuesta, debemos derivar el recuento de votos por opción de los datos en la tabla de votos. Podemos crear una vista de Postgres y rastrearla usando el motor GraphQL para que estos datos derivados estén disponibles en 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;
La vista poll_results
une los datos de las tablas de vote
y poll
para proporcionar un recuento agregado de la cantidad de votos por cada opción.
Usando las suscripciones de GraphQL sobre esta vista, react-google-charts y el componente de suscripción de react-apollo
, podemos conectar un gráfico reactivo que se actualiza en tiempo real cuando ocurre un nuevo voto de cualquier cliente.
subscription getResult($pollId: uuid!) { poll_results(where: {poll_id: {_eq: $pollId}}) { option { id text } votes } }
Integración de la API de GraphQL
Como mencioné anteriormente, utilicé Apollo Client, un SDK de código abierto para integrar una aplicación ReactJS con el backend de GraphQL. Apollo Client es similar a cualquier biblioteca de cliente HTTP, como las solicitudes de python, el módulo http estándar para JavaScript, etc. Encapsula los detalles de realizar una solicitud HTTP (en este caso, solicitudes POST). Utiliza la configuración (especificada en src/apollo.js
) para realizar solicitudes de consulta/mutación/suscripción (especificada en src/GraphQL.jsx con la opción de usar variables que pueden sustituirse dinámicamente en el código JavaScript de su aplicación REACT) para un punto final de GraphQL. También aprovecha el esquema escrito detrás del extremo de GraphQL para proporcionar validación de tiempo de compilación/desarrollo para las solicitudes antes mencionadas. Veamos lo fácil que es para una aplicación cliente realizar una solicitud de consulta en vivo (suscripción) a la API de GraphQL.
Configuración del SDK
El SDK de Apollo Client debe apuntar a un servidor GraphQL, de modo que pueda manejar automáticamente el código repetitivo que normalmente se necesita para dicha integración. Entonces, esto es exactamente lo que hicimos cuando modificamos src/apollo.js al configurar la aplicación de interfaz.
Realizar una solicitud de suscripción de GraphQL (Live-Query)
Defina la suscripción que vimos en la sección anterior en el archivo 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 } }`;
Usaremos esta definición para conectar nuestro componente React:
export const Result = (pollId) => ( <Subscription subscription={gql`${SUBSCRIPTION_RESULT}`} variables={pollId}> {({ loading, error, data }) => { if (loading) return
Cargando...</p>; si (error) regresa
Error:</p>; regreso ( <div> <div> {renderChart(datos)} </div> </div> ); }} </Suscripción> )
Una cosa a tener en cuenta aquí es que la suscripción anterior también podría haber sido una consulta. Simplemente reemplazar una palabra clave por otra nos da una "consulta en vivo", y eso es todo lo que necesita el SDK de Apollo Client para conectar esta API en tiempo real con su aplicación. Cada vez que hay un nuevo conjunto de datos de nuestra consulta en vivo, el SDK activa una nueva representación de nuestro gráfico con estos datos actualizados (usando la renderChart(data)
). Eso es todo. ¡Es realmente así de simple!
Pensamientos finales
En tres sencillos pasos (crear un backend de GraphQL, modelar el esquema de la aplicación e integrar el frontend con la API de GraphQL), puede conectar rápidamente una aplicación en tiempo real completamente funcional, sin perderse en detalles innecesarios como la configuración una conexión websocket. Justo ahí está el poder de las herramientas comunitarias que respaldan una abstracción como GraphQL.
Si ha encontrado esto interesante y desea explorar GraphQL más a fondo para su próximo proyecto paralelo o aplicación de producción, aquí hay algunos factores que puede usar para construir su cadena de herramientas GraphQL:
- Rendimiento y escalabilidad
GraphQL está destinado a ser consumido directamente por las aplicaciones frontend (no es mejor que un ORM en el backend; los beneficios reales de productividad provienen de hacer esto). Por lo tanto, sus herramientas deben ser inteligentes sobre el uso eficiente de las conexiones de la base de datos y deben poder escalar sin esfuerzo. - Seguridad
De lo anterior se deduce que se necesita un sistema maduro de control de acceso basado en roles para autorizar el acceso a los datos. - Automatización
Si es nuevo en el ecosistema GraphQL, escribir a mano un esquema GraphQL e implementar un servidor GraphQL puede parecer una tarea desalentadora. Maximice la automatización de sus herramientas para que pueda concentrarse en las cosas importantes, como crear funciones de interfaz centradas en el usuario. - Arquitectura Por triviales que parezcan los esfuerzos anteriores, la arquitectura de back-end de una aplicación de grado de producción puede implicar conceptos avanzados de GraphQL como unión de esquemas, etc. Además, la capacidad de generar/consumir fácilmente API en tiempo real abre la posibilidad de construir aplicaciones que son resistentes e inherentemente escalables. Por lo tanto, es fundamental evaluar cómo las herramientas de GraphQL pueden optimizar su arquitectura.
Recursos Relacionados
- Puede ver una versión en vivo de la aplicación aquí.
- El código fuente completo está disponible en GitHub.
- Si desea explorar el esquema de la base de datos y ejecutar consultas GraphQL de prueba, puede hacerlo aquí.