When building Java applications with RESTful APIs and database access, developers often face boilerplate-heavy persistence code. Enter Quarkus—a modern, Kubernetes-native Java framework optimized for GraalVM and HotSpot. Combine Quarkus with Panache, and you get a streamlined, developer-friendly way to work with your data layer using JPA and Hibernate.
Quarkus Panache simplifies entity creation, queries, and CRUD operations by removing the verbosity of traditional JPA. This tutorial will guide you through building a complete CRUD REST API using Quarkus, Panache, and PostgreSQL, from setting up your environment to creating endpoints for managing data.
By the end, you’ll have a fully functional backend app with clean, concise data access logic that’s ready to scale or integrate into a larger system.
Prerequisites
-
Java 17+
-
Apache Maven 3.9+
-
Docker (for PostgreSQL)
-
IDE (e.g., VS Code or IntelliJ)
-
cURL or Postman for testing API endpoints
1. Create a New Quarkus Project
Use the Quarkus CLI or Maven to bootstrap the project:
mvn io.quarkus:quarkus-maven-plugin:3.24.5:create \
-DprojectGroupId=com.djamware \
-DprojectArtifactId=quarkus-panache-crud \
-DclassName="com.djamware.resource.FruitResource" \
-Dpath="/fruits" \
-Dextensions="resteasy-reactive, hibernate-orm-panache, jdbc-postgresql"
Then go to the project folder:
cd quarkus-panache-crud
2. Configure PostgreSQL and Application Properties
Update src/main/resources/application.properties
:
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/fruitsdb
quarkus.datasource.username=postgres
quarkus.datasource.password=postgres
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=true
Run a local PostgreSQL container:
docker run --name fruitsdb -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=fruitsdb -p 5432:5432 -d postgres:16
3. Create the Panache Entity
Create src/main/java/com/djamware/model/Fruit.java
:
package com.djamware.model;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.Entity;
@Entity
public class Fruit extends PanacheEntity {
public String name;
public String color;
}
4. Create the Resource Class (REST Controller)
Edit src/main/java/com/djamware/resource/FruitResource.java
:
package com.djamware.resource;
import com.djamware.model.Fruit;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
@Path("/fruits")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class FruitResource {
@GET
public List<Fruit> getAll() {
return Fruit.listAll();
}
@GET
@Path("/{id}")
public Fruit getById(@PathParam("id") Long id) {
return Fruit.findById(id);
}
@POST
@Transactional
public void create(Fruit fruit) {
fruit.persist();
}
@PUT
@Path("/{id}")
@Transactional
public Fruit update(@PathParam("id") Long id, Fruit updatedFruit) {
Fruit fruit = Fruit.findById(id);
if (fruit == null) {
throw new NotFoundException();
}
fruit.name = updatedFruit.name;
fruit.color = updatedFruit.color;
return fruit;
}
@DELETE
@Path("/{id}")
@Transactional
public void delete(@PathParam("id") Long id) {
Fruit fruit = Fruit.findById(id);
if (fruit != null) {
fruit.delete();
}
}
}
5. Run and Test the Application
Run the app:
./mvnw quarkus:dev
Test with cURL or Postman:
curl -X POST -H "Content-Type: application/json" -d '{"name":"Apple","color":"Red"}' http://localhost:8080/fruits
curl http://localhost:8080/fruits
6. Testing the REST API
You can use curl
, Postman, or any REST client to verify the endpoints.
➤ Create a new fruit
curl -X POST http://localhost:8080/fruits \
-H "Content-Type: application/json" \
-d '{"name": "Banana", "color": "Yellow"}'
➤ Get all fruits
curl http://localhost:8080/fruits
➤ Get a fruit by ID
curl http://localhost:8080/fruits/1
➤ Update a fruit
curl -X PUT http://localhost:8080/fruits/1 \ -H "Content-Type: application/json" \ -d '{"name": "Mango", "color": "Orange"}'
➤ Delete a fruit
curl -X DELETE http://localhost:8080/fruits/1
You should see the effects of each operation reflected when querying /fruits
.
7. Packaging the Application
To build a JAR for production:
./mvnw package
The JAR will be located at:
target/quarkus-app/quarkus-run.jar
To run it:
java -jar target/quarkus-app/quarkus-run.jar
8. (Optional) Native Build with GraalVM
If you have GraalVM installed and properly configured, you can compile to a native binary:
./mvnw package -Pnative
Run the native binary:
./target/quarkus-panache-crud-1.0.0-SNAPSHOT-runner
This significantly reduces startup time and memory usage.
9. Containerizing with Docker
Quarkus makes it easy to build a Docker image. Add this to your pom.xml
if you want to use Jib:
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.version}</version>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
</goals>
</execution>
</executions>
</plugin>
Then build the image:
./mvnw clean package -Dquarkus.container-image.build=true
Run it with Docker:
docker run -i --rm -p 8080:8080 quarkus/quarkus-panache-crud
Conclusion
With Quarkus Panache, writing CRUD applications becomes cleaner, more concise, and enjoyable. You’ve built a fully functional REST API connected to PostgreSQL, leveraging modern Java stack features like hot reload, native compilation, and reactive programming support.
Quarkus and Panache reduce boilerplate and accelerate backend development, especially when building microservices or deploying to containers and Kubernetes.
Whether you're developing traditional apps or preparing for serverless or cloud-native deployments, this stack is production-ready and developer-first.
You can get the full source code on our GitHub.
That's just the basics. If you need more deep learning about Quarkus, Java, and Microservices, you can take the following cheap course:
-
(2025) Quarkus for beginners, everything you need to know.
-
Quarkus - Simple REST API and Unit Tests with JUnit 5
-
Cloud-native Microservices with Quarkus
-
Building Microservices with Quarkus
-
K8s Native Java Microservices on Quarkus - 2022 Edition
-
Deep Dive into AWS Lambda for Java Developers
-
Accessing Relational Databases with Quarkus
Thanks!