About cookies on this site Our websites require some cookies to function properly (required). In addition, other cookies may be used with your consent to analyze site usage, improve the user experience and for advertising. For more information, please review your options. By visiting our website, you agree to our processing of information as described in IBM’sprivacy statement. To provide a smooth navigation, your cookie preferences will be shared across the IBM web domains listed here.
Tutorial
Create a simple REST application using Quarkus
A quick start guide for Quarkus app development
Quarkus is a Kubernetes-native Java stack tailored for GraalVM and OpenJDK HotSpot. Quarkus offers incredibly fast boot times, low RSS memory consumption, and a fantastic developer experience with features like live coding.
This quick start guide gets you up and running with Quarkus on macOS, including necessary tools. You will build a basic database application using Quarkus, Java 17, PostgreSQL, and Hibernate ORM Panache. While Hibernate ORM is the standard, powerful Jakarta Persistence implementation capable of complex mappings, it doesn't always make the most common tasks trivial. Hibernate ORM with Panache is Quarkus's solution to this, focusing specifically on making your data entities and repositories simple, straightforward, and fun to write by reducing boilerplate code for common persistence operations.
Step 1. Set up your environment
We'll use standard macOS tools and package managers where possible. These are the essential tools you'll need to complete this tutorial:
- Podman
- SDKMAN!
- Java 17
- Quarkus CLI
- Maven
Optionally, you can also install a code editor.
Set up Podman for container management
Quarkus integrates seamlessly with containers, especially for development services like databases and other sytems. Podman is a daemonless container engine alternative to Docker.
On Mac, each Podman machine is backed by a virtual machine. Once installed, the podman command can be run directly from the Unix shell in Terminal, where it remotely communicates with the podman service running in the Machine VM. It is almost transparent in usage to Docker and easy to use for development.
Install Podman. Find the latest Podman release on the GitHub Release page, and download the binary for your system architecture. At the time of writing, the latest release was v5.4.1. Install either the unversal pkg installer or the arm or amd version.
Initialize and start your Podman Machine. Podman on macOS runs containers within a lightweight Linux VM. Initialize and start it:
podman machine init podman machine startKeep this machine running whenever you need container support (like running the PostgreSQL database later). You can check its status with
podman machine list.Stop your Podman Machine by using this command:
podman machine stop
Install SDKMAN! for managing SDKs
SDKMAN! is a vital tool for managing parallel versions of multiple software development kits (SDKs), including Java and the Quarkus CLI itself on Unix systems including MacOS.
Install SDKMAN!. Open your terminal and run:
curl -s "https://get.sdkman.io" | bashFollow the instructions in the script that guides you. You'll likely need to run
source "$HOME/.sdkman/bin/sdkman-init.sh"or restart your terminal.Verify the installation.
sdk version
Install Java 17 (Eclipse Temurin)
Quarkus requires Java 11 or later. We'll install Java 17 using the popular Eclipse Temurin distribution via SDKMAN!.
Install Java 17.
sdk install java 17.0.14-tem(Note: SDKMAN might list a slightly newer patch version of 17. Use
sdk list javato see available versions and install the latest `-tem` if needed.)*Verify the installation.
java -versionEnsure that it shows the Temurin 17 version you installed.
Install the Quarkus CLI
The Quarkus Command Line Interface (CLI) helps create projects, manage extensions, and run commands.
Install the Quarkus CLI.
sdk install quarkusVerify the installation.
quarkus --version
Install Maven
Quarkus supports both Maven and Gradle as build tools. But for simplicity, let's just use Maven for this tutorial.
Install Maven.
sdk install mavenVerify the installation.
mvn --version
Install a code editor
While not an essential tool for working in Quarkus, you might want to grab an editor of your choice. Quarkus has IDE plug-ins for bothIntelliJ and VS Code.
Step 2. Create your first Quarkus application
Let's create a simple REST application that manages Person entities stored in a PostgreSQL database.
Generate a project using the Quarkus CLI.
quarkus create app getting-started-quarkus \ --extension=resteasy-reactive-jackson,hibernate-orm-panache,jdbc-postgresql \ --maven # Or --gradle if you prefer cd getting-started-quarkusWhat this code does:
create app getting-started-quarkus: Creates a new project directory.--extension=...: Adds necessary Quarkus extensions:resteasy-reactive-jackson: For creating non-blocking REST endpoints with JSON supporthibernate-orm-panache: Simplifies JPA persistence using Hibernate ORM with the active record or repository pattern.jdbc-postgresql: The JdbC driver for PostgreSQL.
--maven/--gradle: Specifies the build tool. We'll use Maven for this tutorial.
Familiarize yourself with the generated project structure.
pom.xml(orbuild.gradle): Project definition and dependencies.src/main/java: Your Java source code.src/main/resources/application.properties: Quarkus configuration file.src/test/java: Test source code.
Step 3. Set up the PostgreSQL database
You'd expect some instructions about how to set up your database for testing in a container and how to configure Podman to run it locally, right? We don't need to do this at all. The magic is called Dev Services in Quarkus. Just forget about it for now and you will see more about the why later in this tutorial.
Step 4. Configure the application
Edit src/main/resources/application.properties to tell Quarkus how to connect to the database.
# Datasource Configuration
quarkus.datasource.db-kind=postgresql
# Hibernate ORM Configuration
quarkus.hibernate-orm.database.generation=drop-and-create # Recreate schema on startup (dev only!)
quarkus.hibernate-orm.log.sql=true # Log SQL statements (useful for debugging)
# Optional: Configure default HTTP port if 8080 is taken
# quarkus.http.port=8081
Note on Quarkus Dev Services: For development, if you don't provide a datasource url, user and password, and you do have a container runtime like Podman running, Quarkus will automatically start a database container for you. This is super convenient and makes your development a lot more fun with a lot less to configure. Learn more about Quarkus' Dev Services, because they extend way beyond just databases.
Step 5. Create the data entity
Define the Person entity using Panache's Active Record pattern.
Create src/main/java/org/acme/entity/Person.java:
package org.acme.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import io.quarkus.hibernate.orm.panache.PanacheEntity; // Use this for auto-generated ID
import java.time.LocalDate;
@Entity // Marks this as a JPA entity
public class Person extends PanacheEntity { // Extends PanacheEntity for simplified db operations + ID
public String name;
public LocalDate birthDate;
// Optional: A constructor for easier creation
public Person(String name, LocalDate birthDate) {
this.name = name;
this.birthDate = birthDate;
}
// Panache requires a default constructor for JPA
public Person() {
}
// PanacheEntity provides common static methods like findbyId, listAll, etc.
// and instance methods like persist(), delete()
// so you don't have to write a single line of code and have more time for commenting the important parts.
}
Step 6. Create the REST resource
Now, let's create the API endpoint that clients (like web browsers or other services) will interact with. In Quarkus, this is typically done using Jakarta REST (formerly JAX-RS) annotations to define HTTP endpoints.
We'll create a PersonResource class that exposes our Person entity over HTTP. The example below implements common CRUD (Create, Read, Update, Delete) operations for managing persons. However, this CRUD pattern is just one common example. You can use these same Jakarta REST annotations (@GET, @POST, @PUT, @DELETE, @PATCH, @Path, @PathParam, @QueryParam, etc.) and techniques to design any kind of RESTful API structure and implement any business logic required by your application, not just simple data management.
Create a JAX-RS resource to expose CRUD operations via HTTP.
Create src/main/java/org/acme/resource/PersonResource.java:
package org.acme.resource;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.acme.entity.Person;
import java.util.List;
@Path("/persons") // Base path for this resource
@Produces(MediaType.APPLICATION_JSON) // Default response type
@Consumes(MediaType.APPLICATION_JSON) // Default request body type
@ApplicationScoped // Makes this a CDI bean
public class PersonResource {
@GET
public List<Person> listAll() {
return Person.listAll(); // Panache static method
}
@GET
@Path("/{id}")
public Response findbyId(@PathParam("id") Long id) {
Person person = Person.findbyId(id); // Panache static method
if (person == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
return Response.ok(person).build();
}
@POST
@Transactional // Required for database modification operations
public Response create(Person person) {
if (person == null || person.id != null) { // Basic validation
throw new WebApplicationException("Person ID must not be set", 422);
}
person.persistAndFlush(); // Panache instance method (persist + commit immediately)
// Return 201 Created status with the location of the new resource
return Response.status(Response.Status.CREATED).entity(person).build();
}
@DELETE
@Path("/{id}")
@Transactional
public Response delete(@PathParam("id") Long id) {
boolean deleted = Person.deleteById(id); // Panache static method
if (deleted) {
return Response.noContent().build(); // 204 No Content
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
}
Step 7. Run the app in development mode
Quarkus shines in development mode with live reload.
Start Quarkus Dev Mode.
quarkus dev # Or ./mvnw quarkus:devAccess the application. Quarkus will start quickly. You'll see output indicating it's running, typically on
http://localhost:8080.Test the endpoints (using
curlor a tool like Postman/Insomnia).Create a Person:
curl -X POST -H "Content-Type: application/json" \ -d '{"name":"Ada Lovelace", "birthDate":"1815-12-10"}' \ http://localhost:8080/persons(You should get a 201 Created response with the created person object including its ID)
Get All Persons:
curl http://localhost:8080/personsGet a Person by ID (replace
1with the actual ID returned):curl http://localhost:8080/persons/1Delete a Person by ID (replace
1with the actual ID):curl -X DELETE http://localhost:8080/persons/1
Do some live coding. Try changing the
PersonResource.javacode (for example, add a log statement). Save the file. Quarkus will detect the change, recompile, and restart the application extremely quickly. Refresh your browser or re-runcurlto see the change without manually restarting.Access the Dev UI dashboard at
http://localhost:8080/q/dev-ui. This is a powerful dashboard showing configured extensions, beans, configuration options, and more.
Step 8. Test the application
Quarkus provides excellent testing support.
Run unit tests. The generated project includes a basic unit test (
src/test/java/org/acme/GreetingResourceTest.java). These run without starting the full Quarkus application.Run integration tests (
@QuarkusTest). These tests run against a fully booted Quarkus application, including database interactions. Quarkus manages the lifecycle automatically.Create
src/test/java/org/acme/resource/PersonResourceTest.java:package org.acme.resource; import io.quarkus.test.junit.QuarkusTest; import io.restassured.http.ContentType; import jakarta.transaction.Transactional; import org.acme.entity.Person; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.time.LocalDate; import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.*; @QuarkusTest // Boots Quarkus before tests run public class PersonResourceTest { // Clean the table before each test method to ensure isolation @BeforeEach @Transactional void cleanDatabase() { Person.deleteAll(); } @Test public void testListAllEmpty() { given() .when().get("/persons") .then() .statusCode(200) .body("$.size()", is(0)); // Assert the list is empty } @Test public void testCreateAndListPerson() { // 1. Create a person String requestBody = """ { "name": "Grace Hopper", "birthDate": "1906-12-09" } """; Person createdPerson = given() .contentType(ContentType.JSON) .body(requestBody) .when().post("/persons") .then() .statusCode(201) // Check for Created status .body("name", is("Grace Hopper")) .body("id", notNullValue()) // Check ID is generated .extract().as(Person.class); // 2. List all persons and verify the created one is there given() .when().get("/persons") .then() .statusCode(200) .body("$.size()", is(1)) // Should be one person now .body("[0].id", is(createdPerson.id.intValue())) // Check ID matches .body("[0].name", is("Grace Hopper")); } @Test public void testFindbyIdNotFound() { given() .when().get("/persons/999") // Use an ID unlikely to exist .then() .statusCode(404); // Not Found } }Run all tests.
./mvnw test # Or quarkus testQuarkus will manage the test lifecycle, potentially starting a test-specific database using Testcontainers if Dev Services is active or using the configured one if explicitly set.
Quarkus does supports continuous testing, where tests run immediately after code changes have been saved. This allows you to get instant feedback on your code changes. Quarkus detects which tests cover which code, and uses this information to only run the relevant tests when code is changed. You can learn even more about this by following the official Quarkus Guide on Continous Testing.
Step 9. Build your app for production
JVM Jar:
quarkus build # or ./mvnw packageThis creates runnable JAR files in the
target/quarkus-app/directory. Run usingjava -jar target/quarkus-app/quarkus-run.jar.Native Executable (Optional): For the smallest footprint and fastest startup, build a native executable using GraalVM.
- Ensure GraalVM (matching your Java version, e.g., 17) is installed and configured correctly. SDKMAN can install GraalVM:
sdk install java 17.0.10-graalce(or similar). Build:
./mvnw package -Pnative # or quarkus build --nativeThis takes longer but produces a native executable (e.g.,
target/getting-started-quarkus-1.0.0-SNAPSHOT-runner) with no JVM dependency.
- Ensure GraalVM (matching your Java version, e.g., 17) is installed and configured correctly. SDKMAN can install GraalVM:
Conclusion
You've successfully set up your development environment for Quarkus and created a simple REST application connected to a PostgreSQL database using Panache. You've experienced live coding, and you've written some tests. Congratulations!
Quarkus offers much more, including advanced configuration, security, messaging, reactive programming models, and extensive cloud-native tooling. Explore the official Quarkus guides to dive deeper. Happy coding!