Building Quarkus Application with MongoDB and Panache
Aasawari Sahasrabuddhe5 min read • Published Dec 03, 2024 • Updated Dec 03, 2024
SNIPPET
Rate this quickstart
Building applications with frameworks and libraries always makes the job simpler. And who doesn't like a simple life when it comes to building complicated applications? When it comes to Java applications, choosing Spring Data as the framework helps you reduce boilerplate code for setting up the configurations. Similar to Spring, to build Quarkus applications, you can make use of Panache. Panache is a Quarkus-specific library that simplifies the development of your Hibernate-based persistence layer.
Panache in Quarkus simplifies data access by minimizing boilerplate with built-in CRUD operations and type-safe queries. It supports both the Active Record and Repository patterns, allowing flexibility in architecture. Panache is tightly integrated with Quarkus, leveraging fast startup times and a low memory footprint, making it ideal for cloud-native and serverless environments. It also provides easy pagination, supports dynamic and named queries, and works seamlessly with Hibernate ORM and JPA, enhancing productivity while keeping code clean and efficient.
In this tutorial, we will be making a Quarkus application with Panache to perform some basic CRUD with aggregations to show the efficiency of working with Panache. The API allows you to perform basic CRUD operations like adding, updating, finding, and deleting books. It also supports aggregation to the group and counts books by genre.
- An IDE of your choice.
- Maven or Gradle as per choice.
Before we get into the actual implementation part of the APIs, let's consider two of the most important things.
- You can create your free cluster by following the steps mentioned in the documentation or using an existing cluster.
- Create a Quarkus project with Maven using the below command.
1 mvn io. quarkus:quarkus-maven-plugin:create \ 2 -DprojectGroupId=io.quarkus.platform \ 3 -DprojectArtifactId=quarkus-bom \ 4 -DclassName="com.example.BookResource" \ 5 -Dpath="/books"
3. Now, add the below dependencies to the pom.xml.
1 <dependencies> 2 <dependency> 3 <groupId>io.quarkus</groupId> 4 <artifactId>quarkus-arc</artifactId> 5 </dependency> 6 <dependency> 7 <groupId>io.quarkus</groupId> 8 <artifactId>quarkus-rest</artifactId> 9 </dependency> 10 <dependency> 11 <groupId>io.quarkus</groupId> 12 <artifactId>quarkus-rest-client-jackson</artifactId> 13 <version>3.16.2</version> 14 </dependency> 15 <dependency> 16 <groupId>io.quarkus</groupId> 17 <artifactId>quarkus-resteasy-reactive-jackson</artifactId> 18 <version>3.15.1</version> 19 </dependency> 20 <dependency> 21 <groupId>io.rest-assured</groupId> 22 <artifactId>rest-assured</artifactId> 23 <scope>test</scope> 24 </dependency> 25 26 <dependency> 27 <groupId>io.quarkus</groupId> 28 <artifactId>quarkus-mongodb-panache</artifactId> 29 </dependency> 30 31 </dependencies>
4. Build the complete application and download the dependencies:
1 mvn clean install
Once the project is set up, the next step is to add the API endpoints. In this tutorial, we will use the Book collection from the Atlas cluster. The model class in the Quarkus project will add the fields inside the collection, and the next step will be to perform operations on the collection.
The Book collection in the Atlas collection will look like the following:
The Book collection in the Atlas collection will look like the following:
1 { 2 _id: .. 3 title: .. 4 author: .. 5 genre: .. 6 year: .. 7 }
- Insert one book: The first step will be to insert the documents inside the collection. To do so, we need to add the function in the resource class in the project. A resource class is a class that implements the PanacheRepository and provides methods to manipulate entities.
Add the below code to the resource class, which injects the repository class to implement the methods, and then create the POST call.
1 2 BookRepository bookRepository; 3 4 public Response addBook(Book book) { 5 bookRepository.persist(book); 6 return Response.status(Response.Status.CREATED).entity(book).build(); 7 }
This would insert a simple document at a time inside the collection.
The BookRepository class with the below code:
1 import com.quarkusWithPanache.model.Book; 2 import io.quarkus.mongodb.panache.PanacheMongoRepository; 3 import jakarta.enterprise.context.ApplicationScoped; 4 5 6 public class BookRepository implements PanacheMongoRepository<Book> { 7 }
- Bulk write: The insert method will insert the documents one by one inside the collection. If there are hundreds or thousands of documents, it will take a long time to insert the documents. To save time and resources with every call, the bulk write will insert all the documents in one go. To do so, add the below code, which will perform the bulk insert.
1 2 3 public Response bulkAddBooks(List<Book> books) { 4 // Prepare documents for bulk write 5 List<Document> documents = new ArrayList<>(); 6 for (Book book : books) { 7 documents.add(new Document("title", book.title) 8 .append("author", book.author) 9 .append("genre", book.genre) 10 .append("year", book.year)); 11 } 12 getCollection().insertMany(documents); 13 14 return Response.status(Response.Status.CREATED).entity(books).build(); 15 }
The getCollection method returns the database and collection name from import com.mongodb.client.MongoCollection package.
- Find all books: Once all the books are inserted inside the collection, the next step is to retrieve the documents from the collection. To get all the books, create the below API call as:
1 2 public List<Book> getAllBooks() { 3 return bookRepository.listAll(); 4 }
- Find one book by ID: To retrieve a specific book from the collection based on the _id, the below API call would fetch a single document that matches the condition.
1 2 3 public Book getBookById( String id) { 4 return bookRepository.findById(new ObjectId(id)); 5 }
- Delete book by ID: Similar to retrieving a single document from the collection, the delete one will delete the single document based on the matching _id. To do so, use the below-created API.
1 2 3 public Response deleteBook( String id) { 4 boolean deleted = bookRepository.deleteById(new ObjectId(id)); 5 return deleted ? Response.noContent().build() : Response.status(Response.Status.NOT_FOUND).build(); 6 }
- Update a book: Finally, to update the document based on the _id value, use the below code with the updated field values.
1 2 3 public Response updateBook( String id, Book book) { 4 Book entity = bookRepository.findById(new ObjectId(id)); 5 if (entity == null) { 6 return Response.status(Response.Status.NOT_FOUND).build(); 7 } 8 entity.title = book.title; 9 entity.author = book.author; 10 entity.genre = book.genre; 11 entity.year = book.year; 12 bookRepository.update(entity); 13 return Response.ok(entity).build(); 14 }
- Aggregation (genre count): Apart from performing basic CRUD operations, if you wish to perform an aggregation operation, you can use the code as below. As an example, this aggregation will perform a genre field and count the genres.
1 2 3 public Response countBooksByGenre() { 4 5 List<Document> pipeline = new ArrayList<>(); 6 pipeline.add(new Document("$group", new Document("_id", "$genre") 7 .append("count", new Document("$sum", 1)))); 8 9 List<Document> result = getCollection() 10 .aggregate(pipeline) 11 .into(new ArrayList<>()); 12 13 return Response.ok(result).build(); 14 }
To test the API, we can use the following
cURL
commands to interact with the server. Make sure the application is running on http://localhost:8080
before sending the requests.1 curl -X POST "http://localhost:8080/books" -H "Content-Type: application/json" -d '{ 2 "title": "Quarkus in Action", 3 "author": "John Doe", 4 "genre": "Programming", 5 "year": 2023 6 }'
1 curl -X POST http://localhost:8080/books/bulk \ 2 -H "Content-Type: application/json" \ 3 -d '[ 4 { 5 "title": "The Midnight Library", 6 "author": "Matt Haig", 7 "genre": "Fiction", 8 "year": 2020 9 }, 10 { 11 "title": "Sapiens: A Brief History of Humankind", 12 "author": "Yuval Noah Harari", 13 "genre": "Non-Fiction", 14 "year": 2011 15 }, 16 ....... 17 ]'
1 curl -X GET "http://localhost:8080/books" | jq
Find one book:
1 curl -X GET "http://localhost:8080/books/672f873b421eaa0c3e4da49f" | jq
1 curl -X DELETE "http://localhost:8080/books/673f81b65750de0757a4bbfb" | jq
1 curl -X PUT "http://localhost:8080/books/672f856f421eaa0c3e4da49e" \ 2 -H "Content-Type: application/json" \ 3 -d '{ 4 "title": "Quarkus in Action", 5 "author": "John Doe", 6 "genre": "Programming", 7 "year": 2021 8 }'
1 curl -X GET "http://localhost:8080/books/aggregate/genre-count" | jq
This would give the output for the count of all the genres.
The complete code for the application is available on the GitHub repository.
In this tutorial, we explored how to harness the power of Panache in a Quarkus application to simplify CRUD operations and implement aggregation queries. By leveraging Panache's built-in functionalities, we reduced boilerplate code, making development faster and more efficient. From inserting single and multiple documents to retrieving, updating, and deleting them, we demonstrated how Panache integrates seamlessly with MongoDB to handle data operations. Additionally, we showcased how to perform aggregations, such as counting books by genre, to extract meaningful insights from data.
Panache's ability to work with Hibernate ORM and JPA, combined with Quarkus's low memory footprint and fast startup times, makes it an ideal choice for building cloud-native, serverless, and highly performant applications. With Panache, developers can focus on business logic while maintaining clean and readable code.
If you wish to learn more about MongoDB and its concepts, like vector search, read our tutorial on MongoDB Vector Search with Quarkus on the MongoDB Developer Center, and find more interesting topics. For any questions related to the above or MongoDB, don’t forget to pay a visit to the MongoDB community forum and be a part of an interesting discussion.
Top Comments in Forums
There are no comments on this article yet.