Quarkus CRUD Example with Panache Data

In this tutorial we will learn how to create a REST CRUD application in Quarkus, starting from a Hibernate Panache Entity. We will show two different approaches: in the first one we will create a REST Resources to map the CRUD methods. Then, we will show how to use REST Data Panache to generate automatically a REST Endpoint for an Entity.

Firstly, if you are new to Panache, we recommend checking this tutorial for some background on Hibernate Panache: Managing Data Persistence with Quarkus and Hibernate Panache

Setting up the Quarkus project

The first step is obviously to create a new Project with your favourite tool. If you are using the Quarkus CLI, then you can initialize a project “crud-demo” as follows:

quarkus create app crud-demo

Then, enter the project crud-demo and add the following extensions:

quarkus ext add hibernate-orm-panache jdbc-postgresql resteasy-reactive-jackson

Coding the Basic Crud Application

We will be using Panache Repository Pattern for our application, therefore, our Model will be a plain and simple Entity with just the field definition in it.

@Entity
public class Ticket  {

    @Id
    @GeneratedValue()
    Long id;

    @Column(length = 20)
    public String name;

    @Column(length = 3)
    public String seat;
   
}

Our Repository Class will implement the PanacheRepository Interface. Therefore, we will be able to use the built-in CRUD methods on the Ticket Entity:

@ApplicationScoped
public class TicketRepository implements PanacheRepository<Ticket> {
}

Finally, the TicketResource is the REST Endpoint that wraps the standard GET, POST, PUT and DELETE methods with the corresponding TicketRepository methods:

@Path("crud/ticket")
@ApplicationScoped
@Produces("application/json")
@Consumes("application/json")
public class TicketResource {

    @Inject
    TicketRepository repository;

    @GET
    @Produces("application/json")
    public List<Ticket> get() {
        return repository.listAll(Sort.by("name"));
    }

    @GET
    @Path("{id}")
    public Response getTicketById(@PathParam("id") Long id) {
        return repository
                .findByIdOptional(id)
                .map(d -> Response.ok(d).build())
                .orElse(Response.status(204).build());
    }

    @POST
    @Transactional
    public Response create(Ticket ticket) {
        if (ticket.id != null) {
            throw new WebApplicationException("Id was invalidly set on request.", 422);
        }

        repository.persist(ticket);
        return Response.ok(ticket).status(201).build();
    }

    @PUT
    @Path("{id}")
    @Transactional
    public Response update(@PathParam("id") Long id, Ticket ticket) {

        return repository
                .findByIdOptional(id)
                .map(
                        t -> {
                            t.name = ticket.name;
                            t.seat = ticket.seat;
                            return Response.status(204).build();
                        })
                .orElse(Response.status(Status.NOT_FOUND).build());
    }

    @DELETE
    @Path("{id}")
    @Transactional
    public Response delete(@PathParam("id") Long id) {
        repository.delete("id", id);
        return Response.status(204).build();
    }

}

Testing the CRUD Endpoint

To test our TicketResource you can use any tool such as curl or Postman. Since our application also includes an import.sql file to have some initial Tickets, we can test the HTTP GET method as follows:

quarkus crud example

Then, checkout the JSON Response:

[
    {
        "name": "Chorus Line",
        "seat": "5B"
    },
    {
        "name": "Mamma mia",
        "seat": "21A"
    },
    {
        "name": "Phantom of the Opera",
        "seat": "11A"
    }
]

Then, try to insert some new data with the HTTP POST method:

quarkus example crud application

Then, by including the Ticket id in the Path, we can update an existing Ticket via the HTTP PUT Method:

how to generate a quarkus crud application

Finally, the HTTP DELETE method will delete a Ticket by including the id in the Path:

Generating Rest Endpoints with Panache

REST Data with Panache automatically creates REST resources based on the interfaces available in your application. To do that, you just need to provide a Resource Interface for each Entity and Repository Classes of your domain.

Firstly, let’s add the quarkus-hibernate-orm-rest-data-panache extension to the initial project:

quarkus ext add hibernate-orm-rest-data-panache smallrye-openapi

We also have added the smallrye-openapi to view all REST Endpoints available when we enable Panache REST Data.

Next, just add an Interface which extends Panache RepositoryResource:

public interface TicketResourcePanache  extends PanacheRepositoryResource<TicketRepository, Ticket, Long> {
}

The above interface will automatically create a REST Endpoint which naps the TicketRepository methods:

If you head to the Swagger UI (http://localhost:8080/q/swagger-ui/) you will see that your application now includes two REST Endpoints:

  • The /crud/ticket Endpoint which maps the classic TicketResource Endpoint
  • The /ticket-resource-panache which maps the Endpoint automatically generated by Panache REST:
quakrus getting started with crud example

You can test the above methods with the Postman script available in the tutorial’s source code. (See bottom of the article).

Conclusion

This article was a walk through the creation of REST CRUD Endpoints to map a Panache Respisitory of Data. In the first part of the article we have discussed how to create a standard REST CRUD Endpoint by injecting the Panache Repository in it. In the second part of the article we have discussed how to generate automatically the REST Endpoint by adding a PanacheRepositoryResource which maps your Repository.

Source code: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/panache-rest-demo