En el anterior artículo vimos las principales características de GraphQL y qué nos ofrece con respecto a REST al implementar un API. En este segundo artículo veremos como implementar GraphQL sobre un API REST ya existente. Como ya sabemos, GraphQL es independiente de la fuente de datos por lo que ésta es una de las maneras de integrar GraphQL en un API, pero no la única.
Requisitos
Desarrollaremos el API en Java, para lo cual usaremos la librería graphql-java. Además, usaremos los siguientes recursos:
- Maven
- Spring Boot
- Spring Data REST
- API REST ya existente. En este caso utilizaremos el API público de test de Star Wars
- Postman
Implementación
En el artículo anterior conocimos los pilares básicos sobre los que pivota GraphQL:
- schema
- type
- query
- mutation
A la hora de realizar la implementación, independientemente del lenguaje, deberemos crear estos elementos. Además, entra un nuevo elemento más, que será el modo en que recuperamos los datos de la fuente. En la librería GraphQL para Java conocemos a este elemento como DataFetcher.
Types
Un Type representa un objeto del dominio del API y como tal, debemos definirlo. Existen los siguientes campos:
- name, que identifica el nombre del tipo dentro del schema
- description, que aporta información sobre el tipo. No es obligatorio
- fields, que definen los diferentes campos o atributos del tipo
Los fields podrán ser tipos escalares propios de GraphQL o podrán referenciar a tipos propios del modelo del API que estamos definiendo.
El caso de los tipos escalares es el más sencillo, ya que tan solo debemos definir su nombre y la forma de recuperarlo. En el caso de que el nombre coincida con el valor existente en el origen de datos, GraphQL realizará automáticamente un mapeo 1:1.
En el caso que queramos indicar un nombre diferente, debemos indicar explícitamente la forma de recuperar ese objeto mediante un DataFetcher. En nuestro ejemplo definiremos los mismos nombres para los atributos que en la fuente de datos.
La definición de fields que referencian a tipos propios del dominio son la tarea más compleja dentro de la implementación de GraphQL debido a que es necesario adjuntar un DataFetcher, el cual cambia dependiendo de la fuente de datos.
En el proyecto hemos declarado los siguientes tipos:
- Film
- People
- Planet
- FilmConnection
- PeopleConnection
- PlanetConnection
DataFetcher
La tarea más delicada a la hora de integrar GraphQL es implementar el modo en el que se recuperan los datos. Para recuperar los datos debemos implementar el método get de la interfaz DataFetcher.
El parámetro del método se refiere al contexto de la ejecución dentro del esquema de GraphQL. Incluye información de los datos recuperados en la ejecución de una query/mutation según el esquema/estructura de datos definido.
Diferenciaremos, además de la fuente de datos, que en nuestro caso será un API REST, los siguientes casos en los que debemos implementar un DataFetcher:
- recuperar un objeto/recurso del dominio
- incluyendo argumentos de entrada
- recuperar una lista de objetos/recursos del dominio
- recuperar un objeto referenciado por otro objeto, donde existen los siguientes casos:
- recuperar un objeto individual
- recuperar una lista de objetos
DataFetcher para un objeto incluyendo argumentos
En este caso, recuperamos un objeto de la fuente de datos mediante un id pasado como argumento.
Definimos el objeto igual que un field cualquiera, añadiendo el argumento y el DataFetcher
Dentro del DataFetcher, debemos recuperar la información del contexto de la ejecución (parámetro DataFetchingEnvironment), en este caso el argumento.
Una vez recuperado, realizaremos la consulta al API REST construyendo la URI del recurso y devolveremos la respuesta, que en este caso tendrá formato JSON.
DataFetcher para una lista – Connection
Las listas son tratadas de una forma especial en GraphQL, conociéndose el tipo como Connection. Este tipo proporciona una serie de argumentos (first, last, before, next) y la variable cursor que facilitan la paginación a la hora de recuperar los elementos.
La recuperación de los elementos será similar a la de un objeto simple, realizando una petición REST al API. La diferencia radicará en el objeto que devuelva nuestro DataFetcher, que será un objeto SimpleListConnection que contiene la lista de objetos recibida en la petición REST.
DataFetcher para un objeto referenciado
El tercer caso es aquel en el que debemos recupera un objeto fruto de una relación con otro. El API que utilizamos como ejemplo devuelve las referencias a los objetos mediante URI’s. En un API REST extraeríamos esta URI y realizaríamos una nueva petición al API para obtener el objeto y completar la información del recurso raíz.
La diferencia con los ejemplos anteriores radica, de nuevo, en el modo de recuperación de la referencia del objeto para después realizar la llamada al API REST.
La referencia debe obtenerse del contexto de ejecución. En este caso, debemos obtener la referencia del recurso padre con el cual tiene la relación, que será el objeto source.
En el código implementado se divide la obtención de este recurso para los casos en que es un objeto simple o una lista de objetos.
Con estos elementos queda definido el diccionario del esquema GraphQL, que contiene los tipos del modelo de datos. En el siguiente artículo completaremos el esquema definiendo las queries y mutations y veremos algún ejemplo de llamada al API con GraphQL.
Se puede consultar y descargar el código del proyecto en el siguiente enlace de github