Agregando APIs GraphQL con Negron

Luis Emilio Velasco Sanchez
The Cocktail Engineering
5 min readOct 15, 2018

--

Desde que Facebook publicó el lenguaje de consultas GraphQL en 2015, la popularidad de esta especificación no ha dejado de crecer.

Ya que GraphQL solo se trata de una especificación de un lenguaje siendo totalmente independiente a la implementación de la misma, no solo existe como alternativa a un diseño RESTful de nuestra API, sino que también podría servir como contrato entre APIs internas de nuestra aplicación o librerías de terceros.

Como todo dentro de la ingeniería de software, es necesario valorar pros y contras antes de tomar una decisión sobre qué tecnología o diseño se quiere implementar. En este post usaremos un ejemplo en el que pueda merecer la pena usar GraphQL frente a una arquitectura REST.

¡A emprender!

Ya tenemos nuestra startup: Frasaza.com. Se tratará de un MaaS (Motivación as a Service), en el que venderemos frases de motivación a un módico precio.

Para nuestro MVP empezaremos con una aplicación web. En el futuro, sabemos que nuestra base de usuarios se va a catapultar y necesitaremos aplicaciones de móvil nativas, de modo que desde el principio vamos a plantear una arquitectura que se adapte a ese crecimiento. Crearemos una SPA (Single Page Application) que consumirá una API que provea un listado de nuestras frases motivacionales.

REST vs GraphQL

Es hora de implementar nuestra API, ¿cómo la implementamos? En nuestra aplicación, las frases estarán compuestas por:

  • Autor, si existiera (este, a su vez, tendría un id y un nombre).
  • Contenido, la frase en sí.
  • Tema, la categoría de la frase.
  • Id, para poder identificar nuestra frase.

Además debemos poder:

  • Filtrar las frases por tema.
  • Filtrar frases por autor.
  • Ver una frase por su id.

Si fuéramos a implementar este servicio de una manera RESTful, tendríamos que crear dos endpoints:

  • GET /quotes/:id: donde :id es el id de la frase.
  • GET /quotes?topic=:topic&author_id=:author_id: donde :topic y :author_id son dos parámetros opcionales.

Además de implementar toda la lógica de validación de parámetros y gestión de errores, sería conveniente utilizar una herramienta como Swagger para documentar nuestra API.

Si usáramos GraphQL, podríamos definir una interfaz con tipos y queries, de modo que nuestra API (de una manera simplificada) constaría de un endpoint con los siguientes tipos:

type Author {
id: UUID!
name: String!
}
enum Topic {
INSPIRATIONAL
MENTORSHIP
ENTREPRENEURSHIP
}
type Quote {
id: UUID!
author: Author
content: String!
topic: Topic!
}

En GraphQL el símbolo ! después de un tipo significa que el campo es obligatorio (como argumento o como campo dentro de un objeto). No solo tenemos tipos, también disponemos de enumeraciones (enum) que nos permiten limitar las opciones de entrada a nuestra API.

Contaremos también con la siguiente Query:

type Query {
quotes(id: UUID, authorId: UUID, topic: Topic): [Quote]
}

La cual, tiene tres parámetros opcionales y devolvería un array de frases. Podríamos consultar el listado de frases de la siguiente manera:

query {
quotes(id: $id, authorId: $authorId, topic: $topic) {
id
content
topic
author {
id
name
}
}
}

Donde $id, $authorId y $topic son variables opcionales que podríamos enviar para filtrar los resultados.

Una de las ventajas de GraphQL, es que nos ofrece una gran flexibilidad, permitiéndonos desde el cliente y sin implementar nada extra en el servidor, hacer una llamada como esta para listar únicamente las frases sin nada más:

query {
quotes(id: $id, authorId: $authorId, topic: $topic) {
content
}
}

Dado que existen librerías que implementan la especificación de GraphQL para casi todos los lenguajes populares, tendríamos toda la gestión de tipos, errores y documentación gratis sin necesidad de implementarla nosotros y pudiendo centrarnos en la lógica de negocio.

Desplegando nuestro MVP

¡Ya ha llegado el día! Hemos desarrollado nuestra API en Ruby y nuestra SPA en React (usando Apollo como cliente de GraphQL) y ya estamos listos para desplegar todo. Salimos a producción y todo pinta genial, nuestra arquitectura es algo similar a esto:

Arquitectura MVP

Queremos feedback

Las ventas van viento en popa, así que queremos añadir una funcionalidad para recibir feedback de nuestras frases, para saber cuáles tienen más éxito.

Como no queremos que esto comprometa nuestro servicio existente, vamos a crear un nuevo servicio aparte. Lo implementaremos en Elixir y como hemos tenido una muy buena experiencia con GraphQL, también implementaremos nuestra API usando GraphQL, con la siguiente mutación:

type Mutation {
giveFeedback(score: Int!, comment: String): String
}

Desarrollamos nuestro servicio y para integrarlo con nuestro frontal, plantearíamos una arquitectura como esta:

Arquitectura con nuevo servicio de Feedback

Sin embargo, un solo cliente de GraphQL no debería atacar más de un endpoint, así esta arquitectura no está soportada por el cliente de Apollo. GraphQL está pensado, en contraposición a REST, para atacar un solo endpoint desde el cliente que consuma nuestros servicios.

Creando Negron

En The Cocktail, hemos pasado por una arquitectura similar y hemos creado una aplicación que nos permite agregar varios endpoints de GraphQL en uno solo, a la que hemos llamado Negron.

Con Negron, nuestra arquitectura quedaría de esta manera:

Arquitectura con Negron

De esta manera, consumiremos una sola API que Negron habilitará para nosotros, unificando las APIs de todos los servicios que le indiquemos, gracias a la funcionalidad conocida como schema stiching.

Usando Negron

Negron puede ser usado directamente empaquetado como un contenedor de Docker tanto como una aplicación de Node.

Si, por ejemplo, nuestros servicios se encuentran en:

  • https://quotes.frasaza.com/graphql
  • https://feedback.frasaza.com/graphql

Podremos levantar Negron https://api.frasaza.com/graphql de la siguiente manera:

docker run \
-e NEGRON_PATH=/grapqhl \
-e PROVIDER_URL_QUOTES=https://quotes.frasaza.com/graphql \
-e PROVIDER_URL_FEEDBACK=https://feedback.frasaza.com/graphql \
thecocktail/negron

Con esto ya tendríamos nuestro proxy funcionado correctamente en el puerto 3000 sin ninguna configuración más.

De este modo ya podríamos ejecutar la query de quotes y la mutación de giveFeedback directamente contra https://api.frasaza.com/graphql.

Consideraciones

Al usar schema stiching, se corre el riesgo de que haya colisión de tipos, mutaciones y queries entre los servicios que se agreguen, así que es muy importante que los nombres de estos no coincidan. De ser así, pasaría a existir únicamente la definición del último servicio que se agregó.

Conclusión

Aunque GraphQL y sus implementaciones ofrecen un gran abanico de herramientas, es muy importante plantear nuestra arquitectura y nuestros requisitos antes de decidir si nos encaja REST, GraphQL u otra tecnología, ya que no en todos los escenarios merece la pena o simplemente no encaja con nuestro planteamiento.

Por otra parte, Negron no está limitado a agregar APIs internas, también puede ser usado para servir de proxy contra APIs de terceros y controlar mejor el tráfico, siendo la mayor ventaja el desplegarlo sin apenas configuración.

Si os gusta el proyecto, tenéis dudas o necesidades que podrían ser implementadas, cualquier contribución será más que bienvenida en nuestro repositorio de GitHub.

--

--