Ejemplo de GraphQL con Quarkus
En entradas anteriores vimos un ejemplo de uso de GraphQL con Spring y DGS de Netflix, hoy en Ejemplo de GraphQL con Quarkus traemos el mismo ejemplo pero realizado íntegramente con Quarkus.
Para poder realizar este ejemplo, vamos a utilizar las librerías propias de Quarkus para la persistencia, como es Panache, y la librería de GraphQL en Quarkus, en lugar de DGS de Netflix como es SmallRye.
¿Qué es GraphQL?
GraphQL es un lenguaje que fue creado por Facebook que nos va a permitir consulta y manipulación de datos para API’s. Esta forma de realizar consultas y obtener respuestas nos va a permiritr obtener únicamente los campos que deseamos. De esta forma, en consultas masivas o respuestas con muchos datos podemos conseguir mostrar únicamente los campos necesarios.
Hands On
Para nuestro ejemplo vamos a usar una base de datos relacional en memoria con H2. Nuestro ejemplo consistirá en tres entidades muy básicas: Bank, User y Account Bank, en donde un usuario tiene una o N cuentas bancarias en un banco y un banco tiene 1 o N cuentas bancarias.
En nuestra aplicación haremos uso de Lombok para eliminar el boilerplate de Java.
Al igual que Spring Data nos proporciona JPA para facilitar el control sobre bases de datos, con Quarkus hacemos uso de Panache. Que através de Hibernate, nos ofrecará las ventajas de JPA y nos facilitará el desarrollo de nuestras entidades. Por lo que si ya estas familiarizado con Spring Data, Panache, guarda muchas similitudes y no debería suponer mucho esfuerzo entenderlo.
Puedes utilizar https://code.quarkus.io/, para generar la estructura de tu proyecto
Dependencias Maven para usar GraphQL en Quarkus
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-graphql</artifactId> </dependency>
La anterior dependencia, smallrye, nos proporciona la lógica necesaria para incorporar GraphQL a nuestra aplicación.
Recuerda que puedes usar el siguiente comando directamente desde tu terminal si ya tienes el proyecto creado:
quarkus:add-extension -Dextensions="graphql"
Dependencias Maven para usar persistencia en Quarkus
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-jdbc-h2</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-orm-panache</artifactId> </dependency>
Como hemos comentado anteriormente, es necesario añadir la dependencia de panache para proporcionar persistencia a nuestra aplicación.
Schema GraphQL y Entidades de Dominio
En nuestro anterior ejemplo realizado con DGS de Netflix y Spring Boot, tuvimos que crear y añadir en la carpeta resources nuestros schemas de GraphQL. Pero en este caso con Quarkus y la librería smallrye, no es necesario la creación de los schemas, tan solo el modelo. Por lo que en este caso únicamente crearemos los objetos de dominio, para mostrar el funcionamiento. Aunque esta no es una buena estructura, sirve para hacer más entendible el ejemplo, este podría ser el típico caso para usar Arquitectura Hexagonal, ya que hemos cambiado la tecnología pero la lógica sigue siendo la misma.
Creación entidad User
Para la creación de la entidad hacemos uso de las anotaciones de lombox y de javax.
@Entity @AllArgsConstructor @NoArgsConstructor @Getter @Setter public class User { @Id @GeneratedValue private UUID id; private String firstName; private String lastName; private int age; private String address; private String country; private String city; @OneToMany(mappedBy = "user") private Set<Account> accounts; @ManyToOne() private Bank bank; }
Creación entidad Bank
@Entity @Data @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode public class Bank { @Id @GeneratedValue private UUID id; private String name; private String country; @OneToMany(mappedBy = "bank") private Set<User> users; @OneToMany(mappedBy = "bank") private Set<Account> accounts; }
Construccion de la entidad account
@Entity @AllArgsConstructor @NoArgsConstructor @Getter @Setter public class Account { @Id @GeneratedValue private UUID id; private String name; private String alias; private BigDecimal amount; @ManyToOne(fetch = FetchType.LAZY) private User user; @ManyToOne(fetch = FetchType.LAZY) private Bank bank; }
Creación Objetos Mutation o Input
Cuando trabajamos con GraphQL vamos a definir una serie de objetos como los input a nuestra aplicación, en este caso vamos a definir una serie de objetos que serán los que se guarden en base de datos. Es decir, necesitamos datos de entrada a nuestra aplicación. Los datos que se definen a continuación serán los datos que se guardarán.
@AllArgsConstructor @NoArgsConstructor @Getter @Setter public class UserInput { private String firstName; private String lastName; private int age; private String address; private String country; private String city; }
@Getter @Setter @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode public class BankInput { private String name; private String country; }
@AllArgsConstructor @NoArgsConstructor @Getter @Setter public class AccountInput { private String name; private String alias; private BigDecimal amount; private UUID userId; private UUID bankId; }
Una vez hemos finalizado todos los objetos de dominio y los input es momento de crear la capa repository que será realizada con PanacheRepository.
Creación de los Repositorios con PanacheRepository
Haciendo uso de PanacheRepository vamos a crear la capa Repository. Esta interfaz implementa métodos de consulta y de guardado.
@ApplicationScoped public class UserRepository implements PanacheRepository<User> { }
@ApplicationScoped public class BankRepository implements PanacheRepository<Bank> { }
@ApplicationScoped public class AccountRepository implements PanacheRepository<Organization> { }
Querys y Mutations con GraphQLApi en Quarkus
Una vez hemos finalizado la creación de las entidades y del repositorio llega el momento de añadir la lógica necesaria para poder realizar consultas y mutations con GraphQL en Quarkus. Para ello, vamos a hacer uso de las siguientes anotaciones:
- @GraphQLApi, esta anotación al principio de la clase indica que será un bean de tipo endpoint en GrapqhQL.
- @Query, indica que este método será una consulta.
- @Mutation, añadido en un método indica que esa petición es una
- @Name, esta anotación será para aquellos métodos que lleven parámetros de tipo input.
Query en GraphQL con Quarkus
A continuación veremos como realizar consultas para el schema account.
@GraphQLApi @RequiredArgsConstructor public class AccountQuery { private final AccountRepository repository; @Query("accounts") public List<Account> findAll() { return repository.findAll().stream().collect(Collectors.toList()); } @Query("account") public Account findById(@Name("id") Long id) { return repository.findById(id); } }
Esta clase, en la que vamos a hacer dos consultas diferentes, la hemos marcado con @GraphQLApi, para indicar que en una case de Query de GraphQL, luego a continuación hemos añadido en los métodos encargados de hacer la query la anotación @Query y con el nombre que se hará la query.
Mutation en GraphQL con Quarkus
El tipo Mutation en GraphQL es aquel que se va a encargar de hacer un guardado o una petición que puede cambiar el estado, por ejemplo lo que sería en REST, un POST, un PATCH o un PUT.
Para poder hacer mutation en GraphQL vamos a añadir en la clase @GraphQLApi, y en cada método que se encarge de hacer mutation añadiremos @Mutation(«nombre»), veamos un ejemplo:
@GraphQLApi @RequiredArgsConstructor public class AccountMutation { private final AccountRepository accountRepository; private final BankRepository bankRepository; private final UserRepository userRepository; @Mutation("createAccount") @Transactional public Account createAccount(@Name("account") AccountInput account) { User user = userRepository.findById(account.getUserId()); Bank bank = bankRepository.findById(account.getBankId()); var accountToSave = new Account(null, account.getName(), account.getAlias(), account.getAmount(), user, bank); accountRepository.persistAndFlush(accountToSave); return accountToSave; } }
Hay que tener en cuenta que en el método hemos añadido @Name, porque estamos añadiendo un input, es decir, queremos guardar una nueva cuenta.
Probando GraphQL con Quarkus
Una vez hemos generado nuestro código, lo primero que vamos a hacer, es ejecutar nuestra aplicación Quarkus.
mvn compile quarkus:dev
Si todo funciona bien deberíamos ver algo así:
__ ____ __ _____ ___ __ ____ ______ --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \ --\___\_\____/_/ |_/_/|_/_/|_|\____/___/ 2021-05-08 16:42:23,139 INFO [io.quarkus] (Quarkus Main Thread) graphql-quarkus 1.0.0-SNAPSHOT on JVM (powered by Quarkus 1.13.3.Final) started in 1.023s. Listening on: http://localhost:8080 2021-05-08 16:42:23,140 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated. 2021-05-08 16:42:23,141 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, jdbc-h2, mutiny, narayana-jta, smallrye-context-propagation, smallrye-graphql] 2021-05-08 16:42:23,141 INFO [io.qua.dep.dev.RuntimeUpdatesProcessor] (vert.x-worker-thread-7) Hot replace total time: 2.384s
GraphQl con Quarkus arranca en el endpoint http://localhost:8080/q/graphql-ui/, con lo que si accedemos a ese endpoint veremos la interfaz de GraphQL para hacer consultas:
Vamos a lanzar una cuantas consultas para ver su funcionamiento, empezaremos con un mutation ya que es necesario primero guardar un bank.
mutation CREATE { createBank(bank: {name: "Santander", country:"Spain"}) { id name country } }
Una vez hemos guardado un bank vamos a hacer una petición para recuperar todos:
{ banks{ id name country } }
Con la query anterior vamos a mostrar todos los banks que hay en nuestra base de datos, en nuestro caso solo una. Los campos que aparecerán en la búsqueda serán aquellos que se indiquen en la query, en este caso vamos a mostrar id, name y country.
y finalmente vamos a probar a hacer una búsqueda de un bank por id.
{ bank(id:1){ id name country } }
Ahora vamos a volver a hacer la misma Query pero añadiendo menos campos en la consulta.
{ bank(id:1){ id } }
En la consulta anterior solo se añade el id en la query, por lo que en la respuesta solo llevará ese campo.
Como hemos comentado casi al principio del artículo, al hacer uso de la librería smallrye, se autogenera los schemas de graphQL sin que sea necesario su implementación. Si quieres ver los schemas generados puedes acceder al endpoint http://localhost:8080/graphql/schema.graphql, para los tipos generados.
Conclusión de Ejemplo de GraphQL con Quarkus
Al igual que pasa con la librería de Netflix DGS, la librería para Quarkus SmallRye, nos proporciona un API para poder utilizar GraphQL en Quarkus mediante anotaciones.
GraphQL ha llegado como un buen sustituto para todas las aplicaciones y arquitecturas que funcionan con servicios REST, aportándo muchas mejoras y facilidades a cualquier API, alguna de esas mejoras la hemos podido ver en nuestro ejemplo. Esto unido a un framework como Quarkus cuya principal ventaja es que se encuentra diseñado para entornos Cloud, hace de ambos una combinación perfecta para crear una buena API.
Si quieres ver el ejemplo completo puedes verlo en nuestro github.
No te olvides de seguirnos en nuestras redes sociales Facebook o Twitter para estar actualizado.