OpenAPI y swagger UI en Quarkus
En este artículo vamos a ver como podemos exponer nuestros servicios en Quarkus a través de OpenAPI haciendo uso de la interfaz que swagger UI nos proporciona.
Sin duda Quarkus es uno de los frameworks más prometedores, debido a su fácil integración con Kubernetes por ejemplo, y a su gran velocidad en los tiempos de arranque. Ya que esta última es una de las características más necesarias cuando trabajamos con aplicaciones en un cloud, debido a que si queremos escalar por ejemplo, un Pod conseguiremos que este se levante a una gran velocidad.
¿Qué es OpenAPI?
OpenAPI o OAS, es un estándar que nos define una especificación, que es totalmente agnóstica al lenguaje. Este estándar nos va a permitir exponer nuestra API através de una interfaz de una manera comprensive para los humanos.
El uso de OpenAPI nos va a ayudar y a permitir generar documentación y entender de una manera fácil y dinámica los servicios que estamos exponiendo. Para facilitar este trabajo y poder ver esa documentación en vivo y poder además interactúar con ella, podemos hacer uso de Swagger UI, la cual nos proporciona una interfaz gráfica que nos muestra los endpoints y documentación de nuestra aplicación.
Dentro de la generación de nuestra API, tenemos diferentes formas de trabajo como por ejemplo API First o Code First.
Crear proyecto Quarkus con OpenAPI
Generación con maven
Para empezar vamos a generar nuestro proyecto Quarkus haciendo uso del generador que Quarkus y maven nos proporcionan:
mvn io.quarkus:quarkus-maven-plugin:1.11.3.Final:create -DprojectGroupId=com.refactorizando -DprojectArtifactId=example-openapi-swaggerui -DclassName="com.refactorizando.example.openapi.swaggerui.CarResource" -Dpath="/cars" -Dextensions="resteasy,resteasy-jackson"
Con este comando maven hemos generado la estructura de nuestro proyecto añadiendo OpenAPI y un endpoint /cars.
Generarción de proyecto REST con Quarkus
Una vez hemos generado la estructura con maven, vamos a generar el resource, o controller con Spring, y el objeto que representa nuestro resource.
A continuación vamos a representar el objeto CarResource, el cual fue generado por maven..
@Path("/cars") public class CarResource { private Set<Car> cars = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>())); public CarResource() { cars.add(new Car(1L, "Ford", "Mustang", "Black")); cars.add(new Car(2L, "Renault", "Megane", "Yellow")); } @GET @Produces(MediaType.TEXT_PLAIN) public Set<Car> list() { return cars; } @POST @Produces(MediaType.TEXT_PLAIN) @Consumes(MediaType.TEXT_PLAIN) public Set<Car> add(Car car) { cars.add(car); return cars; } @DELETE public Set<Car> delete(Car car) { cars.removeIf(existingCar -> existingCar.getId().equals(car.getId())); return cars; } }
Una vez definido la clase CarResource vamos a crear el objeto car.
public class Car { private String name; private String model; private String color; public Car(String name, String model, String color) { this.name = name; this.model = model; this.color = color; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getModel() { return model; } public void setModel(String model) { this.model = model; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
Y debido a que hemos actualizado el API (carResource), vamos a actualizar el test para poder verificar correctamente que todo cambio que realizamos es correctamente verificado.
@QuarkusTest public class CarResourceTest { @Test public void testList() { given() .when().get("/cars") .then() .statusCode(200) .body("$.size()", is(2), "brand", containsInAnyOrder("Ford", "Renault")); } @Test public void testAdd() { given() .body( "{\n" + " \"brand\": \"Ford\",\n" + " \"color\": \"Black\",\n" + " \"id\": 0,\n" + " \"model\": \"Mustang\"\n" + "}") .header("Content-Type", MediaType.APPLICATION_JSON) .when() .post("/cars") .then() .statusCode(200) .body( "$.size()", is(3), "brand", containsInAnyOrder("Ford", "Renault", "Ford"), "model", containsInAnyOrder("Mustang", "Megane", "Mustang")); given() .body( "{\n" + " \"brand\": \"Ford\",\n" + " \"color\": \"Black\",\n" + " \"id\": 0,\n" + " \"model\": \"Mustang\"\n" + "}") .header("Content-Type", MediaType.APPLICATION_JSON) .when() .delete("/cars") .then() .statusCode(200) .body( "$.size()", is(2), "brand", containsInAnyOrder("Ford", "Renault"), "model", containsInAnyOrder("Mustang", "Megane")); } }
Exponer la especificación de OpenApi
A continuación vamos a exponer nuestra especificación de OpenApi en versión 3, para ello primero tenemos que añadir la siguiente dependencia.
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-openapi</artifactId> </dependency>
Esta dependencia, entre otras cosas, integra swagger-ui para poder visualizar nuestra espeficación de forma gráfica.
Arrancamos nuestra aplicación haciendo uso de maven:
mvn compile quarkus:dev
Si es la primera vez que usas quarkus habrás observado que sus tiempos de arranque son increíbles.
Y vamos al siguiente endpoint para ver nuestra especificación, http://localhost:8080/q/openapi. Con este endpoint veremos la especificación en texto plano, pero si ya hemos trabajado y estamos familiarizados con la interfaz de swagger, lo mejor será ejecutar el siguiente endpoint, http://localhost:8080/q/swagger-ui.
Si quieres cambiar el endpoint de openapi y el de swagger, se puede hacer de la siguiente manera en tu application.properties
quarkus.swagger-ui.path=/swagger-ui quarkus.smallrye-openapi.path=/swagger
Con las propiedates anteriores hemos cambiado los endpoint y ahora la especificación estará accesible en el endpoint http://localhost:8080/swagger y la interfaz estará disponible en el endpoint http://localhost:8080/swagger-ui
Conclusión
Hacer uso de OpenApi para exponer nuestra API es de gran ayuda para ver nuestra especificación. En este artículo, OpenAPI y swagger UI en Quarkus, hemos visto como podemos generar nuestra aplicación y poder ver nuestra especificación haciendo uso de swagger y su interfaz o verla por pantalla en texto.
Si quieres ver el ejemplo completo puedes verlo en nuestro Github pulsando aquí.
Si necesitas más información puedes escribirnos un comentario o un correo electrónico a refactorizando.web@gmail.com y te ayudaremos encantados!