Simplify Your Data Access with Quarkus Panache: CRUD Example Included

by Didin J. on Jul 29, 2025 Simplify Your Data Access with Quarkus Panache: CRUD Example Included

Build a Java REST API with Quarkus Panache and PostgreSQL. Simplify CRUD operations using concise, boilerplate-free code. Step-by-step guide included.

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:

Thanks!