
Practical Quarkus Guide for Beginners: 7 Key Solutions to Start Successfully
Introduction
In response to the growing demand in the world of application development, Quarkus has emerged as a leading force that redefines expectations.
In this blog, we will explore and I will guide you on how to approach Quarkus for the first time, a development framework that has become the preferred choice of developers and companies seeking efficient and high-performance Java solutions.
I want to share key solutions for the needs you will surely encounter when starting with this technology. We will review how to quickly launch an application, integrate a database, use a great tool to manage dependencies, error handling, among other things.
I’m Pablo, Software Engineer at Kranio, and I invite you to join me on this challenge! 👋
Before starting with the most valuable tips, we must begin by understanding and using the Quarkus platform to launch our first application.
Go to this link: https://quarkus.io/
Prerequisites:
- Java 11 or higher
- IDE of choice (recommendation: IntelliJ)
- (Make sure your system environment variables are properly configured)
Quarkus platform is an amazing tool that allows us to intuitively select versions, build tools, and dependencies, then with a single click download a complete structure and launch it with these simple commands:
- Maven:
./mvnw quarkus:dev - Gradlew:
./gradlew quarkusDev

If you see this in your console, your application is ready!

Now to the important part, imagine you need to integrate a database to start exploring or developing directly.
Well, H2 is the way.
1. Integrating a database into Quarkus in record time
H2 is an open-source relational database management system (RDBMS) written in Java. It is known for being lightweight, fast, and easy to use, and is a popular choice for Java application development and testing.
When we want to integrate a database like H2, we first have to add the corresponding library, Panache.
These are the 2 dependencies you will need:
./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-hibernate-orm-panache"
./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-jdbc-h2"


It is also important to configure the database in the application.properties file

In this example, the application is communicating with the database. You can see that data is being stored. If you want to see an example of this project, check this repository: restApiQuarkusH2‍​

2. Add dependencies to the ongoing project without risk of impacting or stopping the application.
Imagine that in the previous point, where we integrated H2, the situation was different. Instead of working on a Quickstart that has no logic or other dependencies yet, you are working with a robust application with several dependencies, and you need to add one more without stopping the application, to ensure everything continues working correctly and save valuable time in the process.
Well, for this situation there is a tool called MicroProfile. Let me briefly explain:
MicroProfile is a Java specification aimed at simplifying and standardizing the development of Microservices-based applications in the context of the Java Enterprise Edition (Java EE) platform. For your convenience, Quarkus comes with it integrated by default, so you can use it in various ways. Let’s review one.
MicroProfile can do much more, you can read more about it here: https://microprofile.io/https://microprofile.io/
In the developer console, we have a Menu. It has several options. Typing the letter “h” will access an even more extensive menu.

If we type the letter “d” it will take us to a page in the browser where we can modify all the extensions and configurations of the application.

If we hover over this, an icon will appear that takes us to the guide for each one. Here we have a list of the extensions we are using.
We can also change any configuration of our application. Without exiting or closing the application

3. You need to visualize HTTP responses in a controlled way
When we want to handle errors in Quarkus, we can do it in various ways, since the framework provides mechanisms to handle exceptions and customize behavior in unexpected situations.
Check this code:
@GET
@Path("/maxima")
@Produces(MediaType.TEXT_PLAIN)
public String maxima(){
return Integer.toString(temperatures.maxima());

In this case, what happens is that this is not the correct way to work with REST API; this error, although it reveals what is happening, is not useful for the person/service that will consume the API.
That is why I show you a way that is better aligned:
@GET
@Path("/maxima")
@Produces(MediaType.TEXT_PLAIN)
public Response maxima(){
if (temperatures.isEmpty()){
return Response.status(404). entity( o: "no temperatures available"). build();
 }else {
int temperatureMaxima = temperatures.maxima();
return Response.ok(Integer.toString (temperatureMaxima)) .build();
}
}

It is important that you know how to choose the correct HTTP code for each case. And that, whenever possible, you return a standard structure with the error message.
4. How to decouple business logic from the HTTP controller
To decouple the business logic from the controller, you will use dependency injection; for this, let's review the Service Object pattern.
What happens is that with this injection, you will be automatically instantiating that service in the controller. The annotation @ApplicationScoped is necessary in the class.
@ApplicationScoped
public class GenreRepository implements PanacheRepository {
}
Then you must create a variable of this class GenreRepository and indicate to Quarkus to inject it (which means to supply it automatically) using @Inject. After that, you can use this variable in the code.
public class GenreResource {
  @Inject
  public GenreRepository genreRepository;
Important: If we make a small modification, such as changing the visibility of this variable to private, we could have performance problems and receive warnings from Quarkus. Therefore, I suggest keeping the visibility as private, but at the package level.
5. You have a query that returns too much data, it's time to use pagination
It is common in the life of a developer to face databases with thousands of records, and when we want to return them, we realize that there are so many that it becomes confusing and disorganized.
PanacheQuery will allow you to manage the query you want to send to the database, taking advantage of the power of Hibernate ORM (which is what it uses underneath).
Let's review how to apply pagination to a query to offer fewer results per endpoint and thus not put so much stress on the database.
In the following example, you will implement a method findPage in the class GenreRepository that uses Panache to paginate the results.
@ApplicationScoped
public class GenreRepository implements PanacheRepository {
  public PanacheQuery findPage(int page) {
    Page p = new Page(page - 1, 5);                             // here we define the number of objects that will be on each page; repository can also be used as a service
    var query = findAll(Sort.descending("id"));
    query.page(p);
    return query;
  }
}
In this case, the page has 5 units.
Also in PanacheQuery, the class PaginatedResponse is used to encapsulate information about pagination such as the current page, the total number of pages.
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: This is a method parameter that is associated with the query parameter page in the URL. If not provided, it will default to 1. The rest of the code is a filtered search with the genre name to bring it paginated.
@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. Problems handling formats in your endpoint responses?
Annotations are powerful tools; below I share a bit of their potential
Jackson Annotation:
With the Jackson extension, we can simply forget about converting things to JSON. Jackson knows how to inspect the data types our methods return and convert them to JSON if we indicate so. This way, you can easily create specific entities that correspond to models and return them as results from our endpoints, letting them reach the consumer of our API as if they were normal JSON objects.
If we want to modify how an entity is serialized to JSON, we have the annotations @JsonIgnore, @UpdateTimestamp, @JsonProperty(""), @JsonAlias({”gN” , “n”}) to influence how Jackson serializes our entity, to hide fields or rename them.
- @CreationTimestamp. It will be automatically filled with the current date and time.
- @JsonIgnore. This will ignore this information and not show it in the JSON response.
- @UpdateTimestamp. To keep track of when the last update was made on the entity.
- @JsonProperty("Name"). If we want to change the property name in JSON.
- @JsonAlias({”geneName” , “name”}). It allows you to adapt your code to handle these changes without needing to modify the internal structure of your class.
7. Want to prevent empty data from coming in the body?
One of the most common situations I face when making APIs is maintaining proper data validation. For these cases, the Hibernate Validator extension (Quarkus.io) is useful.
To add it, run the following command at the end of the project.
./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-hibernate-validator"
Now you can add a JSON annotation:
- @NotBlank The string must contain at least one non-whitespace character.
- @NotNull This field or parameter cannot have a null value.
- @Email Field must contain a string recognized as a valid email address.
- @Pattern(regexp = "[a-zA-Z]+") Defines constraints based on regular expressions.
- @Size(min = 2, max = 50) Defines constraints on the size of the string.
- @Min and @Max: Sets minimum and maximum limits for numeric values.
To learn more annotations, check this out: https://www.baeldung.com/jackson-annotations
Conclusion
With all this information, you are ready to face developing in Quarkus without fear, I have given you some of my best tricks! If you still feel you need our help, we will be happy to support you!
Ready to take your Quarkus skills to the next level?
At Kranio, we have experts in Java application development who will help you implement efficient solutions using Quarkus, ensuring scalability and performance of your projects. Contact us and discover how we can drive the digital transformation of your company.
Previous Posts

Augmented Coding vs. Vibe Coding
AI generates functional code but does not guarantee security. Learn to use it wisely to build robust, scalable, and risk-free software.

Kraneating is also about protection: the process behind our ISO 27001 certification
At the end of 2025, Kranio achieved ISO 27001 certification after implementing its Information Security Management System (ISMS). This process was not merely a compliance exercise but a strategic decision to strengthen how we design, build, and operate digital systems. In this article, we share the process, the internal changes it entailed, and the impact it has for our clients: greater control, structured risk management, and a stronger foundation to confidently scale systems.
