Ejemplo de GraphQL para una interfaz de un servicio con Spring Boot y Java

Publicado por pico.dev el , actualizado el .
blog-stack java planeta-codigo programacion
Comentarios

GraphQL es una alternativa a una interfaz REST con las ventajas de poder realizar varias consultas en una misma petición y devolviendo únicamente los datos que requiera el cliente. Es una especificación y hay una implementación para los lenguajes de programación más populares entre ellos Java. Este artículo es una introducción con un ejemplo completo que muestra cómo se hacen consultas y modificaciones en los datos.

GraphQL

Con anterioridad las aplicaciones que lo necesitaban ofrecían una interfaz como un servicio mediante web services, sin embargo, esta tecnología era complicada por usar XML y no de fácil utilización en clientes JavaScript. La evolución que a día de hoy sigue siendo mayoritaria son las interfaces REST que emplean la semántica de los verbos del protocolo HTTP para realizar operaciones de búsqueda, creación, modificación y eliminación y normalmente empleando JSON como formato para intercambiar los datos. Sin embargo, REST no está exento de algunos problemas como la necesidad de realizar varias peticiones a cada uno de los recursos que ofrece si se necesitan datos de varios de ellos, otro es que los datos ofrecidos por los servicios REST está prefijados en tiempo de desarrollo no adaptándose a lo que necesita el cliente. En cierta medida estas dos cosas se pueden implementar en la interfaz REST con algunos parámetros pero requiere codificarlo explícitamente.

Más recientemente ha aparecido otra forma de implementar una interfaz de un servicio con GraphQL considerándose una alternativa mejor a REST que solventa los dos problemas de las interfaces REST anteriores. REST ofrece en varios endpoints los recursos que pueden ser accedidos mediante los verbos HTTP (GET, PUT, POST, DELETE), en GraphQL por el contrario hay un único endpoint, los puntos de entrada al grafo y los tipos que se relacionan entre si que son consultados para obtener los datos con el lenguaje de consulta que ofrece GraphQL.

En GraphQL se define un esquema con la definición de los tipos en la API, se diferencia la obtención de los datos que es realizada por las queries y de las modificaciones que es realizada por los mutators, el esquema se puede definir en un archivo de texto como en este ejemplo o de forma programática con código que es necesario para algunas personalizaciones. Otras tareas que pueden ser necesarias en una API son autenticación que es posible capturando los datos del contexto provenientes en los datos o como cabeceras de la petición posiblemente en forma de token de OAuth y la autorización en la lógica del servicio en base al sujeto autenticado. Se puede usar datos propios con scalar para los cuales se ha de proporcionar una clase que realice la transformación implementando una clase GraphQLScalarType. Posee funcionalidades de introspección y también filtrado, paginación, gestión de errores y cacheo aunque esto último es menos efectivo en GraphQL al depender de los datos a devolver que solicite el cliente.

Para usar GraphQL hay que definir un schema que incluye los tipos, sus propiedades y tipos. También se pueden usar fragmentos para reutilizar partes de la definición de los tipos. Cada type representa una entidad que definen las propiedades que posee ya sean datos escalares o referencias a otras entidades formando de esta manera grafos de objetos, los tipos de las variables que poseen una exclamación al final quiere decir que son opcionales, por defecto todos los datos son distinto de nulo. Las listas se definen con corchetes y el tipo entre ellos.

Una definido el esquema hay que desarrollar los resolvers que son encargados de obtener los datos seguramente de una base de datos externa ya sea una base de datos SQL o NoSQL en este caso utilizando una clase que implementa el patrón repositorio y que abstrae del sistema de persistencia donde se almacenan los datos.

Los mutators son los encargados de procesar las peticiones de modificación.

Usando una aplicación de Spring Boot para ofrecer el servicio hay que realizar la contribución adecuada al contenedor de dependencias, en Java GraphQL se define como un servlet al cual hay que proporcionarle la configuración de los resolvers, mutators, procesador de contexto que en este caso se utiliza para la autenticación y definición del esquema entre otras posibles cosas.

El lenguaje de consulta GraphQL permite consultar el grafo de objetos y recuperar los datos deseados. En el siguiente ejemplo se obtienen los libros, los autores y los libros con los datos de sus autores de una clase que implementa el patrón repository. En el ejemplo los datos del repositorio están definidos en la propia clase de forma estática pero como su función es abstraer de donde se obtienen los datos el cambio sería sencillo para que los obtuviese de una base de datos SQL o NoSQL ya que los cambios estarían encapsulados principalmente en esa clase. Los datos son devueltos en formato JSON.

Una de las ventajas de GraphQL sobre REST es que es posible realizar una única petición lo que en REST podrían ser varias. Por ejemplo, la siguiente consulta obtiene en una única consulta todos los libros, todos los autores y el autor con identificativo 1 de la biblioteca, esto mejora el rendimiento ya que en REST se hubiesen requerido varias peticiones una para obtener libros, otra para los autores y otra para el autor 1. La otra ventaja sobre REST es que se devuelven únicamente los datos que el cliente solicita y no una lista prefijada por el desarrollador de la interfaz.

Las consultas puede hacerse mediante una petición GET o POST de HTTP.

Las peticiones de modificación se envían mediante POST. Este es el caso para añadir un libro a la biblioteca y los casos de que el autor del libro no sea válido o que el usuario que añade el libro no tenga permisos. En el ejemplo los errores no son descriptivos de lo que realmente ha sucedido, habría que hacer el tratamiento de errores adecuado para que los mensajes fuesen más descriptivos.

La forma explicada en las guías de GraphQL para Java es que el mutator reciba los datos y este delegue la funcionalidad en una clase que implemente el patrón repository que abstrae del sistema de almacenamiento (base de datos SQL, NoSQL o cualquier otro), además, este patrón repository o clase de lógica de negocio se recomienda que implemente la funcionalidad necesaria para aplicar la autorización. En el ejemplo aunque de forma sencilla solo en usuario admin tiene permitido añadir libros, en un proyecto es posible realizar la autenticación usando Keycloak como sistema de OAuth, usar el token de OAuth para implementar la autorización y un framework de seguridad como Apache Shiro para aplicar los permisos a las funcionalidades.

Los artículos Autenticación con OAuth y Keycloak en un servicio REST con JAX-RS y Spring Boot y Integrar autenticación OAuth con Keycloak, Shiro, Apache Tapestry y Spring Boot pueden servir como base para añadir autenticación OAuth a un servicio GraphQL con Keycloak.

Finalmente, el archivo de construcción de Gradle del ejemplo con las dependencias necesarias.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.