GraphQL: ¡todos para uno y uno para todos!

GraphQL ha sido creado por Facebook como una alternativa clara a las REST APIS’s. ¡Ojo, una alternativa! En ningún caso hablamos de sustituto. GraphQL ha aparecido con mucha fuerza dentro del panorama tecnológico y por eso debemos prestar gran atención al enfoque que promulga.

Todo comenzó cuando en la parte de desarrollo de movilidad de Facebook se percataron de que se hacían excesivas conexiones con el servidor para cada vista de su aplicación móvil. Este modo de proceder no era viable por el volumen que maneja actualmente Facebook, tanto a nivel de servidor como para la experiencia de usuario de su aplicación.

Agudizaron el ingenio para buscar una solución sencilla para este problema. La necesidad era que, con una única petición, poder obtener toda la información que se necesitase. Ahí nació GraphQL.

Como suele ser natural, las soluciones suelen traer consigo consecuencias colaterales. Por ejemplo, con este enfoque podemos encontrarnos con que el usuario nos solicita datos que corresponderán a 15 consultas en la base de datos en una sola petición. ¡Pero calma! GraphQL nos guía para que “no muramos” a la primera de cambio.

Como toda nueva tecnología aparece cargada de ideas novedosas, las cuales nos pueden ayudar a solucionar problemas de un modo más sencillo. Pero como se suele decir: “no siempre es oro todo lo que reluce”. ¡Vamos a verlo!

¿Qué es GraphQL?

GraphQL es un lenguaje para la realización de consultas y operaciones (mutaciones), diseñado para la que la construcción de aplicaciones SPA sea muy sencilla.

Nos provee de una sintaxis flexible y muy intuitiva. Y aquí destacaría el adjetivo “intuitivo”, las operaciones son claramente semánticas, si creamos un usuario la operación se se llama “createUser”, lo cual todo el mundo comprende perfectamente y no debemos asumir que la petición HTTP es un POST y nos llega un payload con la información.

Como todo framework emergente, GraphQL posee implementaciones para diferentes lenguajes y una comunidad en continuo crecimiento.

Pensando en grafos

En GraphQL la idea es modelar nuestra capa de negocio mediante un grafo, que estará definido por un esquema. Los grafos son estructuras que tienen vértices (nodos) y aristas (conexiones entre elementos).

En la teoría de grafos, que es inmensamente amplia, podemos encontrar los tipos de grafos que existen, sus características, como recorrerlos…

Los grafos son herramientas poderosas para describir procesos y facilitan la conexión entre dichos procesos de un modo más natural, se asemejan mucho al modo que tenemos de pensar.

El diseño mediante grafos consistirá en la definición de “tipos de nodos” y cómo se conectan o relacionan entre ellos. En realidad usa la misma filosofía que la programación orientada a objetos, en la que los tipos (entidades) contienen atributos (otros tipos) y así establecemos las relaciones.

Por ejemplo, de la frase:

John y Sally son amigos y han leído el libro de Bases de datos de grafos

Si os fijáis no tenemos entidades como tal (personas y libros), sino roles y estos son los que nos determinan los nodos de nuestro grafo. Las conexiones son las relaciones que podrán ser unidireccionales o bidireccionales.

Este ejemplo procede del modelado de base de datos orientadas a grafos con Neo4j. A día de hoy las base de datos orientadas a grafos suelen usarse en aplicaciones sociales, recomendadores, email, redes…

¿Qué es esto de “Todos para Uno y Uno para Todos”?

Como sabéis este es el lema de los legendarios “Los tres Mosqueteros”, de Alejandro Dumas y en cierto modo define una de las bases de GraphQL,  la existencia de un único punto de acceso a los datos como se muestra a continuación:

En cuanto a construcción:

  • Todos para uno:
    • El “Todos” serían los consumidores que acceden a las operaciones mediante un único punto de entrada, sea cual sea el tipo de integración que se haga. En el caso de publicarlo como un endpoint http accederían mediante el mapeo “/graphql” cumpliendo lo que la especificación marca.
    • El “Uno” sería nuestro GraphQL Server.
  • Uno para todos:
    • El “Uno” sería nuestro GraphQL Server.
    • El “Todos” serían todos los subsistemas a los cuales ha de acceder el GraphQL Server para resolver las consultas y operaciones  requeridas por el usuario. Al fin y al cabo es una fachada que nos abstrae de lo que hay detrás.

Como has podido observar, el corazón de GraphQL es el GraphQL Server:

  • Será el encargado de interpretar el esquema y hacerlo cumplir.
  • Será el encargado de recoger las consultas y operaciones según marca la especificación.
  • Tendrá una consulta y una mutación denominadas como “root”, que serán el punto de exposición. En nuestro caso QueryRoot nos permitirá consultar simultáneamente marcas, modelos y coches… y para las mutaciones será MutateCars.
  • No estará ligado a un protocolo como en el caso de las REST API’s, pero será posible publicarlo como mejor nos convenga. Por ejemplo, podríamos hacer una adaptación para JMS, RPC… Cuando queramos publicarlo como un endpoint nos dice cómo hacerlo, por lo tanto sea como sea siempre nos vamos a encontrar el mismo comportamiento y la misma nomenclatura.
  • Con independencia de la librería y del lenguaje que usemos para implementarlo:  
    • La política de ejecución de las consultas será en paralelo y las mutaciones en batch, así lo dice la especificación.
    • Las mutaciones se ejecutarán en batch para asegurar el orden, ofreciendo consistencia a los resultados.
    • Como veis, la frase “no es oro todo lo que reluce” se hace patente aquí, ya que la programación asíncrona, multihilo… no es sencilla, y si lo unimos con la orquestación de las operaciones para la obtención de los resultados… se complica más todavía.
    • GraphQL aconseja el uso estratégico de una caché, distribuida si tenemos varios nodos de nuestro servidor, que mejorará sustancialmente el performance.

Por ejemplo podríamos en nuestro caso:

  • Caches de maestros
  • Otras de latencia muy pequeña (5 o 10 segundos) de los elementos operacionales (Car, Model, Brand…) por su identificador, consiguiendo así, muchos menos accesos al sistema de persistencia, microservicios, colas, indexadores…

Adiós a las convenciones, ¡bienvenida especificación de GraphQL!

Con las REST APIS’s, la verdad sea dicha, se ha llegado a un punto de concordia. Un punto común en el que casi todo el mundo asume las convenciones típicas, y más o menos, las respeta.

Pero a veces es demasiado lioso. Si es un PUT es una modificación; eso sí, si es un GET y hay parámetros son un filtro. Y si es un PATCH ¡ya ni te cuento! Si a todo esto unimos las nomenclaturas, snake-case, camel-case… uso de plurales o singulares, path params… para los consumidores no es fácil.

Pero las trabas se incrementan cuando cada empresa tiene su libro de buenas prácticas, el cual posee pequeños o grandes cambios sobre lo anteriormente citado, la integración se puede convertir en una auténtico despropósito.

La propuesta de GraphQL es mucho más clara, sencilla y simple: tener una especificación que obligará a que todo el mundo haga las cosas del mismo modo.

Al igual que como ocurría en la definición del payload de un WebService, mediante un esquema xsd, también aquí definiremos un esquema con el diseño de nuestras entidades y mutaciones (operaciones sobre las entidades, por ejemplo altas, bajas…) las cuales serán accesibles desde nuestro GraphQL Server.

Como vamos a ver a continuación con un ejemplo, es sumamente intuitivo:

Para construir nuestros esquemas debemos conocer lo siguiente:

  • Object Types: son las entidades con las que modelamos y estructuramos  nuestros servicios, en nuestro caso Brand, Model, Car, QueryRoot…

Las mutaciones son las operaciones que no corresponden a una consulta: la creación, modificación, borrado, actualización de la información…

  • Fields: son las propiedades que poseen nuestros Object Types.

Las diferentes implementaciones de GraphQL toman cada atributo como independiente a la hora de recuperarlo, es decir, te permiten definir una consulta propia por cada uno de ellos o decidir si pertenecen a una entidad superior que lo gestione.

Dichas propiedades podrán ser de varios tipos:

  • Object Type: referenciamos a otro objeto, Brand dentro de la definición de Model. La “!” indica que es obligatorio.

  • Escalares: son los tipos básicos: String, Int, Boolean, Float, ID. Permite declarar tipos custom indicando la implementación de la serialización y deserialización.
  • Enumerados: son un tipo especial de escalares que poseen un conjunto de valores establecidos.

  • Listas de elementos:

  • Interfaces: los interfaces son definiciones abstractas de atributos comunes para poder obligar a cumplir estructuras comunes. Muy útil para retornar objetos de diferentes tipos que cumplen la misma interfaz, vamos, el uso típico.

  • Uniones: son parecidas a las interfaces, pero no es necesario indicar los atributos comunes. Es decir, no es necesario que se llamen igual, simplemente que sean del mismo tipo.
  • Tipos Input: son aquellos que nos permitirán pasar valores a las consultas y las mutaciones. En estas últimas los tipos input serán pieza fundamental. Se podrán pasar desde valores simples (escalares) a objetos más complejos. Realmente serán los argumentos a cualquier comportamiento dinámico que queramos ofrecer.

Todos estos elementos unidos nos permitirán definir nuestro esquema.

Las técnicas de modelado son similares a las de programación orientada a objetos, tendremos entidades (nodos) y relaciones (conexiones).

Es muy aconsejable usar un lenguaje cercano al negocio cuando definamos el esquema, esto le hará mucho más intuitivo para los consumidores.

¿Cómo consultamos en  GraphQL?

Para que los consumidores conozcan el API y generen sus propias consultas, emplearemos GraphiQL. Esta herramienta obtiene los metadatos de nuestro esquema (es parte de la especificación) y nos facilitará la generación de las consultas (autocompletado) y su validación.

Apollo y Relay son los dos frameworks para la integración con GraphQL, nos facilitarán la construcción de las consultas y mutaciones.

Ambos nacieron inicialmente para complementar a la librería de Javascript de Facebook (React), aunque a día de hoy poseen integración completa con otros framewors y librerias AngularJS, Angular2.

En vez de plantear algo muy teórico y ser repetitivos, vamos a ir realizando consultas sobre nuestro esquema y describiendo los elementos de GraphQL que intervienen en ellas:

  • Con esta primera consulta obtendremos los fields id y color de todos nuestros coches, podemos observar que el resultado se encuentra dentro de “data” porque la respuesta fue correcta, será “error” cuando no sea así.

  • En este caso obtenemos el modelo del coche también, como veis el usuario puede decidir exactamente los datos que necesita en cada momento.

  • En este caso vemos cómo obtenemos múltiples consultas en una misma petición, esto aumentará el performance de nuestra aplicación.

  • Ahora haremos uso de los argumentos de una consulta. Tendremos dos posibilidades a la hora de pasarlos: con una variable o con el valor directamente.

El uso del mecanismo de las variables, de cara al consumidor, permitirá mantener estructuras en las cuales los valores serán sustituidos de forma dinámica (Apollo y Ready nos ofrecen funcionalidades de este tipo).

Otro característica son el uso de los alias, como veis, los alias creados son car1 y car2. Nos permitirán cambiar el nombre del nodo en el que retornamos la respuesta ya que si no lo hiciésemos, se generaría un error al llamarse los dos nodos igual (car).

  • En este caso vamos a ver el uso de fragmentos para minimizar el código de las consultas y operaciones. Los fragmentos son muy usados con interfaces y uniones. Como se ve en el siguiente ejemplo, hemos agrupado en una porción de código (fragmento) los atributos que necesitamos, de tal modo que centralizamos código.

  • Aquí vemos las mutaciones de GraphQL, que, por decirlo de un modo sencillo, deberían ser las operaciones que no conciernen a una consulta propiamente dicha. Una característica importante de las mismas es la posible elección de los atributos que se retornan del mismo modo que en las invocaciones de las consultas, “consulto justo lo que necesito”.

  • En el siguiente ejemplo podemos observar cómo se pueden ejecutar varias mutaciones, la creación de un coche y la eliminación de otro. El orden de ejecución de las  operaciones será batch, como antes hemos comentado.

Conclusión

Como habéis podido comprobar, GraphQL nos aborda con un conjunto de ideas y características novedosas con respecto a lo que venimos estando acostumbrados:

  • Varias consultas y operaciones en una misma petición.
  • El usuario se crea sus propias consultas.
  • Friendly, sencillo, intuitivo…
  • ¡Adiós convenciones,  bienvenida especificación!
  • No es necesario versionado.
  • Estructuras de datos flexibles. Selección de atributos selectiva en las operaciones y consultas.

En la próxima entrega explicaremos la implementación de un ejemplo completo usando Spring Boot + MongoDB como base. Podrás comprobar por ti mismo todo lo explicado en está introducción teórica.

Arquitecto de profesión, pero con corazón de programador ;) Mi objetivo del día a día es aprender, disfrutar de lo que hago y enriquecerme con las personas que me rodean. Con más de 15 años en el mundo del desarrollo e intentando seguirle el ritmo a las nuevas tecnologías.

Ver toda la actividad de J. Manuel García Rozas

Comentarios

  1. Enrique dice:

    Gracias Manuel por tu aporte, muy interesante saber de GraphQL. Quiero preguntarte si es posible implementar o agregar GraphQL en un ambiente Windows ( IIS, Sql Server, ASP.NET ) ?

    Puesto que WCF es lo que se manejaba en este tipo de Tecnologia con Microsoft…

Escribe un comentario