Las APIs más populares que utilizamos a día de hoy son RESTful APIs o un pseudo estándar ad hoc HTTP inventado bajo demanda en ciertos proyectos. La necesidad de avanzar más rápido en productos cada vez más complejos, más allá de un simple CRUD, ha empujado un cambio en la forma en que interactuamos con las APIs. Aquí es dónde surge GraphQL, un fuerte candidato a sustituir a REST, sobre todo en el ecosistema de APIs para apps en mobile.
¿Qué hay de malo en REST? Nada en su concepción inicial y en el contexto dónde surgió, pero desde que fuera definido la forma de interactuar con las APIs ha cambiado. Vamos a repasar las razones por las que deberíamos repensar las tradicionales APIs basadas en RESTful en favor de GraphQL.
RESTful APIS versus GraphQL APIs
Obviamente las APIs REST han sido uno de los puntos determinantes para el auge del desarrollo de apps y de servicios distribuidos, tal como lo conocemos actualmente. Son “el pegamento” que no puede faltar en cualquier app.
REST es un claro caso de éxito, pero su concepción CRUD basada en resources, verbos y códigos de respuesta HTTP denotan cada vez más sus limitaciones y su inflexibilidad.
Principales debilidades de RESTful
En RESTful utilizamos una URI para leer o escribir un único recurso, ya sea, por ejemplo, un producto, una persona, un post o un comentario. Si necesitamos trabajar con múltiples recursos como un listado de posts con un listado de comentarios, necesitamos manejar múltiples endpoint y encadenar distintas llamadas, a veces de forma secuencial. Es en las apps donde recae la responsabilidad de unir y mergear cada recurso.
¿Cuántas veceas os habéis encontrado con la tediosa tarea de “pintar” una pantalla con la información procedente de 4 o 5 endpoint diferente? Por ejemplo, solicitar los ids de los comentarios de un post concreto para luego pintarlos en la misma pantalla junto a él. Aquí por ejemplo, necesitamos una request al detalle del post, a las estadísticas del post, al listado de comentarios, etc… después juntar toda esa información.
Cuando hacemos una request, no recibimos simplemente la información que necesitamos, sino todo el conjunto de datos relativos al resource alojado en esa URI. Algo bastante complicado de gestionar y costoso en recursos cuando el 90% de los datos procedentes de cada resource/endpoint son innecesarios. Por ejemplo, si solo queremos obtener el nombre de una persona al hacer la consulta recibiremos datos como, por ejemplo, su fecha nacimiento o su profesión. Datos innecesarios que desde el lado cliente no podemos controlar y entorpecen la obtención de información necesaria.
El versionado de una API REST no es trivial. En muchas ocasiones necesitamos añadir nuevos campos o modificar el tipo de alguno de ellos pero para poder dar soporte de retrocompatibilidad los incluimos en el payload que los clientes viejos, lo cual hace crecer el payload de respuesta innecesariamente. O bajo otra estrategia, desde el lado del servidor gestionamos “vistas distintas” según la versión del cliente que consume el recurso. A veces eso nos lleva a innumerables if, codigo spagetti que poluciona el código original de respuesta.
Todos conocemos los verbos HTTP involucrados en las peticiones RESTful pero eso no asegura su correcta utilización. Ni mucho menos de sus códigos error basados en HTTP. Hay una decena de ellos representados a lo largo de las diversas categorías de 2XX, 3XX, 4XX o 5XX. Incluso algunos de ellos han ido surgiendo bajos las necesidades, pero no se utilizan de forma homogénea (a veces de forma laxa y poco estricta).
Por supuesto, las respuestas de error con el payload que reciben los clientes no se quedan atrás en la incoherencia de muchas APIs REST que consumimos. No hay una especificación que lo fije. Lo cual requiere que cada API necesite ser acompañada de una documentación para el desarrollador, corriendo el riesgo de ser incompleta, desactualizada o violada sin previo aviso desde la parte del servidor.
Para aliviar las idas y venidas obteniendo datos de un endpoint a otro muchos desarrolladores se lanzan al acuerdo explícito entre backend y mobile de crear sus propias APIs adhoc. Una pseudo mezcla del RESTful más estricto en algunos endpoint a un libertinaje de payload que componen vistas saltándose uno de los principales reglas de RESTful.
Lo que parece una buena solución puede convertirse en un auténtico quebradero de cabeza de mantener polucionado todo de código duplicado, abstracciones y la recurrente deficiente retrocompatibilidad. Los clientes que utilizan estos custom endpoint no pueden romper el contrato implícito fijado con lo que al final es imposible eliminar campos y los nuevos deben ser añadidos, aunque los consumidores ni los utilicen.
Con endpoint creados bajo el libre albedrío de una API custom, adhoc no tiene un sistema formalizado de tipos perdiendo la potencia de herramientas como Swagger para documentar o gRPC para crear idiomatic client apis.
¿Por qué utilizar GraphQL en nuestras APIs?
GraphQL es una de las alternativas que han surgido para solucionar la mayor parte de estos problemas arriba descritos con API REST.
El primer borrador del RFC (aún en desarrollo) que especifica GraphQL fue creado por Facebook. Tal como ellos mismo explican llevan usándolo y perfeccionandolo desde 2012 en sus apps móviles. Y no son los únicos, también Github, Pinterest o Shopify se han unido al carro. Todos los interesados en implementar GraphQL pueden colaborar en su definición, es Open Source.
GraphQL es un protocolo agnóstico y no depende en nada de HTTP. No usa métodos o respuestas HTTP. Por supuesto, sigue siendo el canal más popular para comunicarse entre consumidores de GraphQL.
Unas de las principales características de GraphQL es que el lenguaje y sintaxis usado en la request es el mismo que el de la respuesta. Analizando el JSON podemos comprender claramente el diccionario de key-value. Simplificando su uso podríamos decir que es un lenguaje pregunta/respuesta expresada en relaciones entre los objetos expuestos. Además GraphQL dispone de un sistema de tipado fuerte por el que cualquier mal uso puede ser rápidamente detectado en tiempo de desarrollo (incluso desde el IDE) en contraposición a un mapeo clásico de JSON.
Otro de sus principales rasgos es que la API puede ser single endpoint. Es decir, un único endpoint puede manejar sin problemas las peticiones de los clientes. Todas ellas puede preguntar sobre múltiples recursos y campos, tan sólo indicando qué necesitan. La respuesta será acorde a esta demanda de información, ni más ni menos.
Estas son algunas de las razones que hacen GraphQL eficiente, efectivo y fácil de usar. Pero su principal razón de ser es cambiar la concepción hasta ahora utilizada alrededor de los clientes móviles bajo un modelo Declarative Data Communication.
Los cliente móviles necesitan tener más poder de decisión sobre los datos que necesitan consumir. Esto les provee de mayor independencia sobre los servicios de datos. Las queries son enviadas desde el cliente lo que simplifica la retrocompatibilidad explícita.
Debemos evitar en la medida de los posible los clásicos viajes de datos entre cliente y servidor para componen ciertos datos requeridos para componer un vista única. El diseño GraphQL permite manejar jerarquías de objetos para componer las vistas que necesitemos de ellos, según los requisitos solicitados por el cliente.
Ejemplo de API GraphQL frente a una API tradicional REST. Creando una app sobre Starwars
Para comprender las diferencias entre API REST y GraphQL hay un excelente ejemplo utilizando la API de Starwars de personajes para construir una pequeña aplicación que consuma información de ambas APIs.
Aquí tenéis cada una de ellas para trastear por vuestra cuenta:
API REST: SWAPI
API GraphQL: SWAPI GraphQL Wrapper
Vamos a imaginar la vista de una app que pretender representar la ficha de los personajes de Starwars que muestran básicamente estos campos: * Nombre del personaje * Fecha de nacimiento * Lugar de nacimiento * Películas en las que aparece
Utilizando la API REST necesitaremos componer la vista con las siguientes request y sus correspondientes response:
- Obtenemos la información del personaje, por ejemplo, Luke Skywalker
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
"skin_color": "fair",
"eye_color": "blue",
"birth_year": "19BBY",
"gender": "male",
"homeworld": "http://swapi.co/api/planets/1/",
"films": [
"http://swapi.co/api/films/6/",
"http://swapi.co/api/films/3/",
"http://swapi.co/api/films/2/",
"http://swapi.co/api/films/1/",
"http://swapi.co/api/films/7/"
],
"species": [
"http://swapi.co/api/species/1/"
],
"vehicles": [
"http://swapi.co/api/vehicles/14/",
"http://swapi.co/api/vehicles/30/"
],
"starships": [
"http://swapi.co/api/starships/12/",
"http://swapi.co/api/starships/22/"
],
"created": "2014-12-09T13:50:51.644000Z",
"edited": "2014-12-20T21:17:56.891000Z",
"url": "http://swapi.co/api/people/1/"
}
- Información de su lugar de nacimiento
{
"name": "Tatooine",
"rotation_period": "23",
"orbital_period": "304",
"diameter": "10465",
"climate": "arid",
"gravity": "1 standard",
"terrain": "desert",
"surface_water": "1",
"population": "200000",
"residents": [
"http://swapi.co/api/people/1/",
"http://swapi.co/api/people/2/",
"http://swapi.co/api/people/4/",
"http://swapi.co/api/people/6/"
],
"films": [
"http://swapi.co/api/films/5/",
"http://swapi.co/api/films/4/"
],
"created": "2014-12-09T13:50:49.641000Z",
"edited": "2014-12-21T20:48:04.175778Z",
"url": "http://swapi.co/api/planets/1/"
}
- Películas en las que ha participado (n peticiones por cada película)
Después de combinar estas 6 respuestas JSON procedentes del servidor podremos satisface los datos requeridos por la vista. Anotamos dos grande problemas: el número de request/response diferentes que debemos que manejar y la cantidad de datos innecesarios que recibimos sin necesitarlos en el payload de la respuesta.
Por otro lado, utilizando el ejemplo de API GraphQL vamos obtener esos mismos datos.
Como comentábamos en el artículo, somos capaces de seleccionar los datos que queremos obtener. Básicamente podemos a partir del esbozo de datos de los requisitos de nuestra pantalla podemos construir la siguiente query de GraphQL:
{
person(personID: 1) {
name
birthYear
homeworld {
name
}
filmConnection {
films {
title
}
}
}
}
Lo que recibimos de respuesta se asemeja completamente a nuestra petición rellenando los valores de cada campo, tal que así:
{
"data": {
"person": {
"name": "Luke Skywalker",
"birthYear": "19BBY",
"homeworld": {
"name": "Tatooine"
},
"filmConnection": {
"films": [
{
"title": "A New Hope"
},
{
"title": "The Empire Strikes Back"
},
{
"title": "Return of the Jedi"
},
{
"title": "Revenge of the Sith"
}
]
}
}
}
}
Una de las herramientas más potentes que dispone GraphQL es GraphiQL, un sandbox para hacer consultas a la API aprovechando su carácter fuertemente tipado que nos ayuda a construir las queries y descubrir semánticamente los campos y relaciones disponibles.
Este artículo sólo ha sido una introducción a GraphQL. Nuestra intención ha sido hacer una comparación entre las tradicionales APIs REST y GraphQL.
Por supuesto, tenemos que adentrarnos en la implementación de GraphQL en backend y el ecosistema de librería, tools que dispone y sobre todo su relación con Relay y React en próximos artículos. Si queréis conocer más sobre el proyecto la web oficial de GraphQL mantenida por Facebook contiene gran cantidad de recursos.
Ver todos los comentarios en https://www.genbeta.com
VER 0 Comentario