3 Reasons Why All Java Developers Should Consider Quarkus
Quarkus is an open source stack to write cloud-native Java applications freaky fast. Live coding, automatic service provisioning, and more.
There’s one sure way to kill developer productivity.
As a consultant, I worked with a global financial company to help them build a credit card approval process.
But every time I committed code, I had to wait 15 minutes to see a single change. And while 15 minutes is plenty of time to make a cup of coffee, it also discourages innovation.
Nothing kills developer productivity like slow feedback loops.
Which is why we use Quarkus.
Java + Developer Speed = Quarkus
Quarkus helps developers to build Java applications faster. It’s an open-source Kubernetes Java framework for building cloud-native applications.
Everything in Quarkus was designed with developer speed in mind: from live coding to fast startup times to automatic external service provisioning. But I’m getting ahead of myself.
Setting Up Your Quarkus Project
We’ll demonstrate the most compelling developer features of Quarkus by building a REST API. You’ll need to have three things installed:
- Maven 3.8.1+ & JDK 11+
- Docker Desktop
- Quarkus CLI (Complete “Installing the CLI”)
First, we’ll set up a new quarkus project. Quarkus has a CLI that makes many of these operations easy. Open a terminal and create a new project.
quarkus create app org.codelikethewind:quarkus-dev-joy:1.0.0 && cd quarkus-dev-joy
As we’ll see later, this creates a sample REST application with one endpoint.
1. Rapid Development with Live Coding
As developers, we need to be able to test out ideas and pivot quickly. Quarkus Dev Mode enables live reload, so when you modify your code and refresh your browser, these changes will automatically take effect.
In your terminal, navigate to the project directory and start the application in dev mode.
quarkus dev
After the server starts, go to http://localhost:8080/hello. You should see a greeting.
Now, let’s change the greeting without restarting the server. In your favorite IDE, open src/main/java/org/codelikethewind/GreetingResource.java.
package org.codelikethewind;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/hello")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello from RESTEasy Reactive";
}
}
Change the return statement to “Hello [Your Name]”.
public String hello() {
return "Hello T.O";
}
Refresh the webpage and...boom! You should see your new greeting. No re-compile. No restart.
It’s a simple app, but the Quarkus team says that most applications live reload in about a second. That’s every developer’s dream.
2. Easy Testing With Automatic External Service Provisioning
Whether I'm building something new or reproducing a bug, I don't want to spend time setting up external services. Especially when they're likely to be torn down that day.
If you have Docker installed, Quarkus Dev Services will automatically set up external services if it finds a matching 3rd party configuration.
We’ll add a find function to our API. But first, we’ll need a database. Open the terminal to your project root (quarkus-dev-joy) and add three extensions.
quarkus ext add quarkus-hibernate-orm-panache quarkus-jdbc-postgresql resteasy-reactive-jsonb
Quarkus extensions are really just additions to the pom file. Here, we're using three of them:
- Hibernate for object relational mapping (and Panache for search functions)
- PostgreSQL driver to connect to our database
- Json serialization for our API
Next, we’ll create an entity to save to the database. In the same folder as GreetingResource, create a file called Car.java.
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import javax.persistence.Column;
import javax.persistence.Entity;
@Entity
public class Car extends PanacheEntity {
@Column(name="make")
public String make;
@Column(name="model")
public String model;
@Column(name="year")
public String year;
}
This entity has three columns and extends PanacheEntity. Panache simplifies a lot of boilerplate code for entities. In this case, we don’t need to add any default getters and setters.
Now we’ll define our REST endpoint. Create a class called CarResource.java.
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;
@Path("/car")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class CarResource {
@GET
public List<Car> getAll(){
return Car.findAll(Sort.ascending("make")).list();
}
}
Here, we define our rest endpoint path (/car). It has a single method to find all cars in our database, sorted by make.
Finally, we need to tell Quarkus to use a PostgreSQL database. In src/main/resources, open application.properties and add our database configuration.
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.format-sql=true
quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.sql-load-script=data.sql
quarkus.datasource.db-kind=postgresql
And we’ll also create an import script to seed our database with some car data. Create a file called src/main/resources/data.sql
insert into car(id, make, model, year) values (nextval('hibernate_sequence'), 'Nissan', 'Pathfinder', '1997');
insert into car(id, make, model, year) values (nextval('hibernate_sequence'), 'Mercedes-Benz', 'GLS', '2008');
insert into car(id, make, model, year) values (nextval('hibernate_sequence'), 'Ford', 'F150', '2020');
insert into car(id, make, model, year) values (nextval('hibernate_sequence'), 'Jeep', 'Wrangler', '2017');
insert into car(id, make, model, year) values (nextval('hibernate_sequence'), 'BMW', 'X5', '2013');
insert into car(id, make, model, year) values (nextval('hibernate_sequence'), 'Toyota', 'Camry', '1997');
insert into car(id, make, model, year) values (nextval('hibernate_sequence'), 'Honda', 'Civic', '2002');
insert into car(id, make, model, year) values (nextval('hibernate_sequence'), 'Hyundai', 'Sonata', '2013');
Open your browser and go to http://localhost:8080/car
You should see all the cars we added printed out. But, do you remember starting a database?
That’s the power of Dev Services in Quarkus. It detected that we had a PostgreSQL database configuration and started it for us (usually via TestContainers.
Right now, Dev Services supports databases, Kafka, Redis, and a few more.
3. Faster Error Resolution With Friendlier Stack Traces
We spend a lot of time reading stack traces. And usually, the root cause of the error is at the bottom of the stack. That means time spent scrolling and looking instead of coding. Quarkus reversed that so the most helpful parts of the stack trace show up first.
So, let’s create an error. Open our REST resource file (CarResource.java) and change the sort parameter to an invalid column called “makes”.
@GET
public List<Car> getAll(){
return Car.findAll(Sort.ascending("makes")).list();
}
Go to http://localhost:8080/car
You should see a stack trace telling you that we’ve used an invalid column. Usually, this would appear at the bottom. You can click the reverse link to get the regular stack trace.
It’s the little things.
Bonus: You Don’t Have to Learn a New Language
Everyday a new framework is born – which is great. But it often means learning a new set of tools. And while we love to learn, we also want to spend time building. One of the best reasons to try out Quarkus is that it’s still Java. It’s faster, but still Java.
Quarkus Runtime Performance
In many cases, Quarkus applications will startup 4-10 times faster than a traditional Java application while using a fraction of the memory.
This may seem like magic, but it is by design. Quarkus does at build time what traditional Java does at runtime. This includes:
- Classpath scanning
- Configuration parsing
- Initializing components
Quarkus applications only contain classes needed at runtime. This pre-processing results in less memory usage and a much faster startup time. You can learn more about that here.
Best Practices
There are two modes of running Quarkus. You can either run it natively (using GraalVM) or on the JVM. Both give you speed, memory, and performance gains. But if you’re trying to pick between the two, here’s a short-hand guide.
Choose the JVM → Highest throughput, lowest response times, best tooling
Choose Native → Fastest startup time, lowest memory footprint and when you don’t need reflection
Recap
Quarkus helps us create cloud-native Java applications fast. It enables us to live code, automatically spins up external services, and provides friendlier stack traces for quick error resolution.
Project Code & Links