In this article, we will see how we can expose our services in Quarkus using OpenAPI with the help of the Swagger UI interface.
Undoubtedly, Quarkus is one of the most promising frameworks due to its easy integration with Kubernetes, for example, and its fast startup times. The latter is a crucial feature when working with applications in the cloud because it enables rapid scaling of Pods.
What is OpenAPI?
OpenAPI or OAS is a language-agnostic standard that defines a specification allowing us to comprehensively expose our API with a human-friendly interface. Using OpenAPI helps us generate documentation and easily understand the services we are exposing. To visualize and interact with this documentation, we can make use of Swagger UI, which provides a graphical interface displaying the endpoints and documentation of our application.
There are different ways to work with API generation, such as API First or Code First.
Creating a Quarkus Project with OpenAPI
Project Generation with Maven
To begin, let’s generate our Quarkus project using the generator provided by Quarkus and Maven:
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"
With this Maven command, we have generated the structure of our project, adding OpenAPI and an endpoint /cars
.
Generating the REST Project with Quarkus
Once we have generated the structure with Maven, let’s create the resource (controller) with Spring and the object representing our resource.
Now, we define the CarResource
class, which was generated by 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; } }
Next, let’s create the Car
object since we have updated the API (CarResource). The following class represents the Car
object:
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; } }
As we have updated the API, we need to update the test to ensure that all changes we made are properly verified:
@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")); } }
Exposing the OpenAPI Specification
Next, we will expose our OpenAPI specification in version 3. To do this, we first need to add the following dependency:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-openapi</artifactId> </dependency>
This dependency, among other things, integrates Swagger UI to visualize our specification graphically.
Now, let’s start our application using Maven:
mvn compile quarkus:dev
If it is your first time using Quarkus, you will notice its incredible startup times.
To view our OpenAPI specification, navigate to http://localhost:8080/q/openapi. This endpoint will display the specification in plain text. If you are familiar with the Swagger interface and want to view the specification interactively, use the following endpoint: http://localhost:8080/q/swagger-ui.
If you wish to change the OpenAPI and Swagger endpoints, you can do so in your application.properties
:
quarkus.swagger-ui.path=/swagger-ui quarkus.smallrye-openapi.path=/swagger
With the above properties, the specification will be accessible at http://localhost:8080/swagger, and the interface will be available at http://localhost:8080/swagger-ui.
Conclusion
Using OpenAPI to expose our API is very helpful to view our specification. In this article, “OpenAPI and Swagger UI in Quarkus,” we have seen how to generate our application and view the specification using Swagger with its graphical interface or in plain text.
If you want to see the complete example, you can find it on our GitHub by clicking here.