Quarkus para Principiantes: Soluciones Prácticas para 7 Escenarios Iniciales

Introducción

En respuesta a la creciente demanda en el mundo del desarrollo de aplicaciones, Quarkus ha surgido como una fuerza líder que redefine las expectativas.

En este blog, exploraremos y te acompañaré en cómo abordar Quarkus por primera vez, un marco de desarrollo/Framework que se ha convertido en la elección predilecta de los desarrolladores y empresas en busca de soluciones Java eficientes y de alto rendimiento.

Quiero compartirte soluciones claves para las necesidades que seguramente te encontrarás al querer iniciarte en esta tecnología. Revisaremos cómo levantar una aplicación rápida, integrarle una base de datos, usaremos una grandiosa herramienta para manipular dependencias, manejo de errores, entre otras cosas.

¡Soy Pablo, Software Engineer en Kranio, y te invito a acompañarme en este desafío! 👋

Antes de comenzar con los trucos más valiosos debemos partir por entender y utilizar la plataforma de Quarkus para levantar nuestra primera aplicación.

Ingresa en este link: https://quarkus.io/

Pre-requisitos:

  • Java 11 o +
  • IDE a elección (recomendación: IntelliJ)
  • (Asegúrate de tener bien configurada tus variables de entorno a nivel sistema)

Quarkus platform es una herramienta increíble, nos permite seleccionar de forma muy intuitiva las versiones, herramienta de compilación y dependencias, para luego con un solo click descargar una estructura completa y levantarla con estos simples comandos:

  • Maven: ./mvnw quarkus:dev
  • Gradlew: ./gradlew quarkusDev

¡Si ves esto en tu consola, tu aplicación está lista!

Ahora vamos a lo importante, imagina que necesitas integrar una base de datos para comenzar a explorar o desarrollar directamente.

Bueno, H2 es el camino.

1. Integrando una base de datos a Quarkus en tiempo récord

H2 es un sistema de gestión de bases de datos relacional (RDBMS) de código abierto, escrito en Java. Es conocido por ser ligero, rápido y fácil de usar, y es una opción popular para el desarrollo y pruebas de aplicaciones Java.

Cuando queremos integrar una bd como H2, primero tenemos que agregar la librería correspondiente, Panache.

Estas son las 2 dependencias que necesitarás:


./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-hibernate-orm-panache"

./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-jdbc-h2"


Es importante configurar también la base de datos en el archivo application.properties

En este ejemplo la aplicación se está comunicando con la base de datos. Al ver que se está almacenando datos. Si quieres ver un ejemplo de este proyecto, revisa este repositorio: restApiQuarkusH2

2. Agregar dependencias al proyecto en curso sin riesgo de impactar ni bajar la aplicación.

Imagina que en el punto anterior, en el que integramos H2, la situación fuese diferente. En lugar de trabajar sobre un Quickstart que aún no tiene lógica ni otras dependencias trabajas con una aplicación robusta con varias dependencias, y tienes la necesidad de agregar una más sin bajar la aplicación, para asegurar que todo siga funcionando correctamente y ganando tiempo valioso en el proceso.

Bien, para esta situación existe una herramienta llamada MicroProfile. Te explico brevemente:

MicroProfile es una especificación de Java destinada a simplificar y estandarizar el desarrollo de aplicaciones basadas en MicroServicios en el contexto de la plataforma Java Enterprise Edition (Java EE). Para tu conveniencia en Quarkus viene integrada por defecto, así que podrás hacer uso de esta de distintas maneras, revisemos una.

MicroProfile puede hacer mucho más, puedes leer mas sobre eso aquí: https://microprofile.io/https://microprofile.io/

En la consola de desarrollador tenemos un Menú. En él, varias opciones.  Colocando la letra “h”  accederemos a un menu aún más extenso.

Si ponemos la letra “d” nos dirigirá a una página en el  navegador donde podemos modificar todas las extensiones y configuraciones de la aplicación.

Si nos posicionamos sobre esta aparecerá un icono donde nos lleva a la guía de cada uno.Aquí tenemos una listas de las extensiones que estamos utilizando.

También podemos cambiar cualquier configuración de nuestra aplicación. Sin salir ni cerrar la aplicación

3. Necesitas visualizar de manera controlada las respuestas http

Cuando queremos hacer un manejo de errores en Quarkus, podemos hacerlo de diversas maneras, ya que el framework proporciona mecanismos para manejar excepciones y personalizar el comportamiento ante situaciones inesperadas.

Revisa este código:



@GET
@Path("/maxima")
@Produces(MediaType.TEXT_PLAIN)
public String maxima(){
return Integer.toString(temperatures.maxima());


Cuando lo dejamos como String y no tiene parámetros nos arroja un error.

En este caso lo que sucede que no es la manera correcta de trabajar con REST API, ese error si bien nos delata que está pasando, para la persona/servicio que consumirá la API no es útil.

Por eso te muestro una manera que se encamina mejor:


@GET
@Path("/maxima")
@Produces(MediaType.TEXT_PLAIN)
public Response maxima(){
			if (temperatures.isEmpty()){
					return Response.status(404). entity( o: "no hay teperaturas"). build();
		  }else {
					int temperatureMaxima = temperatures.maxima();
					return Response.ok(Integer.toString (temperatureMaxima)) .build();
			}
}


Es importante que sepas elegir el correcto código HTTP para caso. Y qué, en lo posible, devuelvas una estructura estándar con el mensaje de error.

4. Cómo desacoplar la lógica de negocio del controlador HTTP

Para desacoplar la lógica que negocio del controlador, usarás la inyección de dependencias, para esto revisemos el patrón Service Object.

Ocurre que con esta inyección, estarás instanciando automáticamente ese servicio en el controlador. La anotación @ApplicationScoped es necesaria en la clase.


@ApplicationScoped
public class GenreRepository implements PanacheRepository {
}


Luego debes crear una variable de esta clase GenreRepository y le indicamos a Quarkus que la inyecte (lo cual significa que la suministre automáticamente) usando @Inject. Después, podrás usar esta variable en el código.



public class GenreResource {
    @Inject
    public GenreRepository genreRepository;

Importante: Si hacemos una pequeña modificación, cómo cambiar la visibilidad de esta variable a private, podríamos tener problemas de rendimiento y recibir advertencias de Quarkus. Por eso, te sugiero mantener la visibilidad como private, pero a nivel de paquete.

5. Tienes una query que te responde demasiados datos, es hora de usar la paginación

Es común en la vida como desarrollador enfrentarte a bases de datos con miles de datos, y cuando queremos devolverlos nos damos cuenta de que son tantísimos que se vuelve confuso y desordenado.

PanacheQuery te permitirá cuidar la query que quieres enviar a la base de datos, aprovechando el poder de Hibernate ORM (que es lo que lleva debajo).

Revisemos como aplicar paginación a una query para poder ofrecer menos resultados por endpoint y así no someter a tanto estrés a la base de datos.

En el siguiente ejemplo vas a implementar un método findPage en la clase GenreRepository  que utiliza Panache para paginar los resultados.


@ApplicationScoped
public class GenreRepository implements PanacheRepository {
    public PanacheQuery findPage(int page) {
        Page p = new Page(page - 1, 5);                                                         //aqui definimos la cantidad de objetos q estaran en cada pagina repository tambien lo podemos ocupar como servicio
        var query = findAll(Sort.descending("id"));
        query.page(p);
        return query;
    }
}


En este caso la página tiene 5 unidades.

También en PanacheQuery, la clase PaginatedResponse se utiliza para encapsular la información sobre la paginación como la página actual, el total de página actual.


public record PaginatedResponse"E"(int page, int totalPages, List"E" data) {
    public PaginatedResponse(PanacheQuery"E" query){
        this(query.page().index + 1, query.pageCount(),query.list());
    }
}

@QueryParam("page") @DefaultValue("1") int page: Este es un parámetro de método que se asocia con el parámetro de consulta page en la URL. Si no se proporciona, se establecerá por defecto en 1. El resto del código es de una búsqueda filtrada con el nombre de género para traerla paginada.


@GET
public PaginatedResponse"Genre" list(
			@QueryParam("page")@DefaultValue("1")intpage,
			@QueryParam("q") String q) {
	var query = genres. findPage (page) ;
	¡f (a != null) {
			var nameLike = "%" + q + "%";
			query.filter ( filterName: "name.like", Parameters.with( name: "name", nameLike));
	}
	return new PaginatedResponse"" (query);
}


6. ¿Problemas para manejar formatos en las respuestas de tus endpoints?

Las anotaciones son herramientas poderosas, a continuación te comparto un poco de su potencial

Anotación Jackson:

Con la extensión Jackson podemos simplemente olvidarnos de convertir las cosas a JSON. Jackson sabe inspeccionar los tipos de datos que nuestros métodos devuelven y convertirlos a JSON si se lo indicamos. De este modo, podrás fácilmente crear entidades específicas que se corresponden con modelos y devolverlos como resultado de nuestros endpoints, para dejar que lleguen al consumidor de nuestra API como si fuesen objetos JSON normales.

Si queremos modificar la forma en la que se serializa a JSON una entidad, tenemos las anotaciones @JsonIgnore, @UpdateTimestamp, @JsonProperty(""), @JsonAlias({”gN” , “n”}) para influir en la forma en la que Jackson serializa nuestra entidad, para ocultar campos o renombrarlos.

  • @CreationTimestamp. Se llenará automáticamente con la fecha y hora actuales.
  • @JsonIgnore. Esto ignorará esta información y no la mostrará en la respuesta del Json.
  • @UpdateTimestamp. Para llevar un registro de cuándo se realizó la última actualización en la entidad.
  • @JsonProperty("Name"). Si queremos cambiar la propiedad del name en Json.
  • @JsonAlias({”geneName” , “name”}). Te permite adaptar tu código para manejar estos cambios sin necesidad de modificar la estructura interna de tu clase.

7. ¿Quieres impedir que venga un dato vacío en el body?

Una de las situaciones más comunes a las que me enfrento al hacer APIs es mantener una correcta validación de los datos. Para estos casos es útil la extensión Hibernate Validator (Quarkus.io).

Para agregarlo, ejecuta el siguiente comando al terminar del proyecto.



./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-hibernate-validator"

Ahora puedes agregar una anotación JSON:

  • @NotBlank   La cadena debe contener al menos un carácter que no sea un espacio en blanco.
  • @NotNull Este campo o parámetro no puede tener un valor nulo.
  • @Email Campo debe contener una cadena que sea reconocida como una dirección de correo electrónico válida.
  • @Pattern(regexp = "[a-zA-Z]+") Define restricciones basadas en expresiones regulares.
  • @Size(min = 2, max = 50) Define restricciones sobre el tamaño de la cadena.
  • @Min y @Max: Establece límites mínimo y máximo para valores numéricos.

Para conocer mas anotaciones, revisa esto: https://www.baeldung.com/jackson-annotations

Conclusión

Con toda esta información estás listo para enfrentarte sin miedo a desarrollar en Quarkus, te he dado algunos de mis mejores trucos! ¡Si aun así sientes que necesitas nuestra ayuda, estaremos felices de apoyarte!

¡Contáctanos!

Pablo Trujillo

April 4, 2024