Funcionalidades básicas de GraphQL
En el capítulo anterior veíamos cómo crear un proyecto con Hasura y Docker en cuestión de minutos, …
leer másHemos visto las funcionalidades básicas para gestionar nuestros datos que GraphQL nos ofrece.
Las operaciones como Query
, Mutation
y Subscription
de GraphQL nos permiten obtener, crear, actualizar y eliminar
datos con una sintaxis muy sencilla y fácil de aprender.
Ahora vamos a ver las diferentes opciones que Hasura nos proporciona por defecto para gestionar la base de datos, ejecutar tareas en el lado del servidor o conectar con servicios externos.
Para este ejemplo vamos a descargar una base de datos ya creada con datos de muestra,
concretamente la que contiene información de un negocio de alquiler de películas llamada Pagila
.
Simplemente tenemos que importar el esquema y los datos de ejemplo con psql
. Después podemos crear la conexión a la base
de datos virtualizada desde el panel de Hasura como vimos en el primer capítulo de la serie.
En el fichero docker-compose tenemos que añadir la cláusula ports
al apartado de postgres
para poder conectar desde fuera
del contenedor virtualizado:
version: '3.6'
services:
postgres:
image: postgres:12
ports:
- "5432:5432"
volumes:
...
Es la primera pantalla que nos encontramos al entrar en el panel de administración de Hasura y en ella tenemos varias partes bien diferenciadas,
en la parte superior está la dirección URL de nuestro endpoint
y los parámetros incluidos en la cabecera de la petición de la API.
A la izquierda el explorador para navegar por las diferentes entidades, GraphiQL
para realizar consultas en el centro y
la documentación dónde consultar los diferentes parámetros y opciones disponibles a la derecha. Abajo escribiríamos los
valores de las variables en formato JSON si se realizan peticiones con variables en GraphiQL
.
En el anterior capítulo ya vimos múltiples ejemplos que podemos trasladar y adaptar para ejecutar directamente desde aquí o podemos utilizar el explorador para construir nuestra consulta con las diferentes opciones de filtrado, selección y ordenación del mismo.
Nada más acceder a la segunda pestaña nos encontramos con un administrador de datos a la izquierda y a la derecha las tablas de las que se está haciendo seguimiento. Abajo a la izquierda hay un botón que nos abrirá una ventana para ejecutar consultas SQL de forma manual.
Al seleccionar alguna de las entidades en el administrador de la izquierda se nos mostrarán los registros que la tabla contiene además de permitirnos añadir nuevas filas o modificar las existentes, las relaciones entre las entidades y los permisos de acceso a los datos a través de la API.
En este punto simplemente tenemos que rellenar los campos para introducir un nuevo registro en la tabla seleccionada.
La pestaña Modify
nos permite cambiar los parámetros de las diferentes columnas de la tabla así cómo añadir nuevas,
los triggers, las restricciones, índices y demás, también nos permite establecer la tabla como un tipo enum
para datos fijos
o crear un campo calculado.
Nos permite gestionar las relaciones entre las diferentes entidades de nuestra base de datos de forma independiente.
La sección de roles y permisos sirve para establecer los controles de acceso a los datos a través de las consultas a la API, quién puede insertar, acceder, modificar o eliminar qué datos y cómo, las variables que puede consultar, etc. Esto utilizando un sistema de roles y permisos lo suficientemente flexible cómo para cubrir la mayoría de casos de uso básicos.
Por ejemplo un rol de usuario simple con acceso de consulta y modificación limitada, otro de tipo moderador que pueda modificar algunas tablas que el usuario no o un rol de usuario para los empleados que tengan permitido el acceso a determinadas tablas que el usuario básico y los de moderación no, etc.
Si seleccionamos en la izquierda la tabla city
podremos crear los roles de usuario, en el campo de texto que pone Enter new role
(Introducir nuevo rol) escribimos user
y pulsamos sobre la cruz roja que hay en la columna Select
, esto nos abrirá el
formulario de configuración de los permisos:
Row select permissions
: Nos permite establecer algún filtro utilizando las variables de la cabecera de la solicitud u otros
campos de la base de datos, lo dejamos sin restricciones activando Without any checks
.Column select permissions
: Indica qué parámetros de la entidad se verán influenciados por esta regla. Presionamos el botón
Toggle all
para que nos active todos los campos haciéndolos así accesibles para este rol.Aggregation query permissions
: Nos permite indicar si este rol de usuario tiene permisos para hacer consultas de agregación,
como totales o promedios, máximos o mínimos. Podemos dejarlo como esta´.Root field permissions
: Para gestionar los permisos del elemento raíz, si está desactivado se permiten todos por defecto.De esta forma habremos creado el nuevo rol de usuario user
con permisos de selección en la tabla city
sin ningún tipo de
restricción en los parámetros a seleccionar, ahora podemos repetir el proceso para crear el rol staff
y darle los permisos de
inserción, selección y modificación necesarios.
Para verificar que los permisos se están comprobando correctamente podemos ir a la pestaña API
y añadir a la cabecera de
la petición la clave x-hasura-role
con el valor user
, veremos que sólo podemos recoger los datos de city
mientras que si lo cambiamos por staff
podemos acceder (además de insertar y modificar) a mayores a la información de la
tabla staff
.
Una acción es un evento que se ejecuta en el servidor tras una llamada a la API, nos permite ejecutar determinadas acciones que no corresponderían con las habituales, fuera de la lógica de negocio.
Imaginemos que en una de nuestras aplicaciones queremos mostrar el tiempo actual en una ciudad concreta, podríamos llamar directamente
a Free Weather API de OpenMeteo
desde el cliente o crear una acción en nuestro Hasura que actuaria
de intermediaria y se encargaría de solicitar a OpenMeteo
la temperatura cuando la llamásemos desde el cliente.
Este tipo de funcionalidades están pensadas para realizar gestiones relacionadas con nuestros datos que pueden requerir de algún tipo de operación especial, tal y cómo lo estamos haciendo ahora tiene el inconveniente de que se realizan dos peticiones en vez de la única que necesitaríamos si hiciéramos la petición directamente. Aunque aunque también tiene la ventaja de que de producirse un error o requerir algún cambio en la petición no sería necesario actualizar las aplicaciones cliente.
Es responsabilidad del equipo de desarrollo estimar ventajas y desventajas para decidir qué método es mejor para la casuística del proyecto en cuestión, esto es sólo un ejemplo.
Para empezar vamos a definir el formato de la acción, la solicitud que se realizará, en el recuadro Action Definiton
.
type Query {
getLatLongTemperature(
latLongInput: LatLongInput!
): LatLongOutput
}
En el siguiente recuadro Type Configuration
creamos los tipos de datos, podríamos asociarlos con el concepto de Clase
de la programación orientada a objetos, definen la estructura de los datos que se recibirán (Input
) y responderán (Output
)
durante la ejecución de la acción.
input LatLongInput {
latitude: Float!
longitude: Float!
}
type LatLongOutput {
latitude: Float!
longitude: Float!
current_weather: CurrentWeatherOutput
}
type CurrentWeatherOutput {
temperature: Float!
windspeed: Float!
winddirection: Float!
weather_mode: Int!
time: String
}
Tanto LatLongOutput
como CurrentWeatherOutput
(que es un subconjunto de datos del primero) se corresponden a la respuesta que recibiremos de la API de OpenMeteo
, que es similar a la que se muestra a
continuación:
{
"latitude": 51.5,
"longitude": -0.120000124,
"generationtime_ms": 0.2110004425048828,
"utc_offset_seconds": 0,
"timezone": "GMT",
"timezone_abbreviation": "GMT",
"elevation": 27.0,
"current_weather": {
"temperature": 5.0,
"windspeed": 5.9,
"winddirection": 259.0,
"weathercode": 0,
"time": "2023-01-30T20:00"
}
}
Aquí recogemos las variables que puede que vayamos a utilizar en algún punto en alguno de los clientes, después en cada petición podremos especificar qué datos queremos exactamente en cada caso.
En el Webhook Handler
tenemos que añadir la URL del endpoint
al que se enviará la petición, la de la API de OpenMeteo
: https://api.open-meteo.com
Más abajo vamos a selecionar la opción para modificar las opciones de la solicitud (Add request options transform
) que
se envía a OpenMeteo
para indicarle que queremos el tiempo actual de la latitud y longitud que le enviaremos.
Request method
: GET{{$base_url}}
: /v1/forecastcurrent_weather
: truelatitude
: {{$body.input.latLongInput.latitude}}longitude
: {{$body.input.latLongInput.longitude}}Utilizando {{$body.input.latLongInput.latitude}}
accedemos al valor de la latitud en el cuerpo de la solicitud de entrada, una vez
hayamos terminado debería mostrarnos una dirección temporal en el Preview
de abajo:
https://api.open-meteo.com/v1/forecast?longitude=10&latitude=10¤t_weather=true
Enlace que podemos consultar directamente en el navegador para comprobar que funciona y devuelve los datos que esperamos.
Ahora podemos probar la acción desde el panel de administración de Hasura, en la pestaña API
introducimos la siguiente consulta:
query GetLatLongTemperature {
getLatLongTemperature(latLongInput: {latitude: 10.4806, longitude: 66.9036}) {
latitude
longitude
current_weather {
temperature
time
weather_mode
winddirection
windspeed
}
}
}
Podríamos también limitar el acceso a esta función utilizando los permisos igual que con las tablas de la base de datos.
Suponiendo que la temperatura fuera una funcionalidad sólo disponible para los usuarios del personal se la restringimos
para que sólo su rol pueda acceder desde la pestaña Permissions
dentro de las opciones de la acción.
Con los esquemas remotos podemos conectar esquemas de GraphQL de otras fuentes por si tuviéramos alguna API GraphQL a mayores y quisiéramos unificarlas.
Los eventos a diferencia de las acciones se ejecutan automáticamente al realizarse una operación en la base de datos ya sea
desde la API o desde cualquier otra fuente, el equivalente a los triggers
en PostgreSQL.
Se utilizan principalmente para llamar a funciones serverless
o webhooks
que se ejecutarán cuando se produzca un evento.
Para poder probarlo vamos a entrar en Webhook.site y copiar la URL que nos proporciona para ponerla
en la configuración de nuestro evento.
Rellenamos el formulario de la siguiente forma:
Event Name
(Nombre): Le he puesto “cityInsertEvent” pero puedes ponerle el nombre que quierasDatabase
(Base de datos): “Films DB”, el nombre que le hayamos puesto al crearlaSchema
(Esquema): “public”Table
(Tabla): “city” o la tabla en la que queramos que se ejecute el eventoTrigger Operation
(Operación): Seleccionamos Insert
para que nos notifique cada vez que se inserta una nueva ciudadWebhook URL
(URL del Webhook): La dirección URL proporcionada por Webhook.siteAhora basta con insertar desde cualquier medio (el panel de Hasura mismo) una ciudad nueva para que nuestro evento se ejecute y nos notifique automáticamente en el servidor que le hemos indicado con la URL. En el panel de Webhook.site nos aparecerá toda la información de la solicitud recibida con los datos que se insertaron si todo ha ido bien.
Al seleccionar el evento que acabamos de crear en el panel de administración de Hasura podemos ver las solicitudes procesadas, pendientes y los logs que guardan un registro de las llamadas a la función.
Los Cron triggers
nos permiten ejecutar eventos periódicamente, por ejemplo para realizar una limpieza de la base de datos
y los One-off scheduled events
nos permiten ejecutar eventos programados en un momento determinado.
Hemos hecho un repaso a casi todas las funcionalidades disponibles en el panel de control de Hasura y hemos incidido en partes como las acciones, eventos y roles ya que son partes no tan fundamentales como la gestión de tablas por defecto pero aportan un valor añadido que puede ser muy útil en muchos casos.
Desde las consultas directamente en el explorador de la API hasta la gestión de roles y permisos, una poderosa herramienta que conllevaría muchas horas de trabajo para ser desarrollada de forma independiente.
Las acciones nos permiten ejecutar una función predefinida y normalmente ajena a la lógica de negocio de la aplicación, muy parecido a lo que hacen los eventos, enviar llamadas a funciones sin servidor u otros puntos externos sólo que de forma automática tras producirse algún evento en la base de datos.
Quizá te puedan interesar
En el capítulo anterior veíamos cómo crear un proyecto con Hasura y Docker en cuestión de minutos, …
leer másGraphQL es un lenguaje de consultas y manipulación de datos de código abierto para datos nuevos o …
leer másDocker es una plataforma de virtualización que ofrece una metodología única para empaquetar y …
leer másDe concepto a realidad