Getting started with MongoDB and Quarkus
Learn how to build high-performance NoSQL applications using Quarkus 3.x and MongoDB. This guide demonstrates both low-level programmatic database operations via the MongoDB Java Client and object-oriented persistence using MongoDB with Panache, fully upgraded with Jakarta EE (jakarta.*) and RESTEasy Reactive.
This tutorial covers all the steps required for creating a REST application with MongoDB NoSQL Database and Quarkus 3.x.
MongoDB is a document-oriented NoSQL database which became popular in the last decade. It can be used for high volume data storage as a replacement for relational databases. Instead of using tables and rows, MongoDB makes use of collections and documents. A Document consists of key-value pairs which are the basic unit of data in MongoDB. A Collection, on the other hand, contains sets of documents and functions, which is the equivalent of relational database tables.
In order to get started with MongoDB, you can download the latest Community version from: https://www.mongodb.com/try/download/community
To simplify things, we will start MongoDB using Docker with just one line:
docker run -ti --rm -p 27017:27017 mongo:7.0
Done with MongoDB, there are basically two approaches for developing MongoDB applications with Quarkus 3.x:
- Using the MongoDB Java Client which focuses on the low-level client API provided by MongoDB driver.
- Using MongoDB with Panache which simplifies repository or active record design patterns using
PanacheMongoEntity.
Using the MongoDB Java Client API
By using this approach, you manipulate plain Java Bean classes with the MongoDB Java Client. Let’s build a sample application. We will start from the simple Model class:
package com.mastertheboss.model;
public class Customer {
private Long id;
private String name;
public Customer() {
}
public Customer(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Then, we need a Service class using Jakarta CDI annotations (jakarta.enterprise.context.ApplicationScoped and jakarta.inject.Inject) to manage standard CRUD operations on the Model using the low-level MongoClient:
package com.mastertheboss.service;
import com.mastertheboss.model.Customer;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import org.bson.Document;
import org.bson.conversions.Bson;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import static com.mongodb.client.model.Filters.eq;
import static com.mongodb.client.model.Updates.set;
@ApplicationScoped
public class CustomerService {
@Inject
MongoClient mongoClient;
public List<Customer> list() {
List<Customer> list = new ArrayList<>();
try (MongoCursor<Document> cursor = getCollection().find().iterator()) {
while (cursor.hasNext()) {
Document document = cursor.next();
Customer customer = new Customer();
customer.setName(document.getString("name"));
customer.setId(document.getLong("id"));
list.add(customer);
}
}
return list;
}
public void add(Customer customer) {
Document document = new Document()
.append("name", customer.getName())
.append("id", customer.getId());
getCollection().insertOne(document);
}
public void update(Customer customer) {
Bson filter = eq("id", customer.getId());
Bson updateOperation = set("name", customer.getName());
getCollection().updateOne(filter, updateOperation);
}
public void delete(Customer customer) {
Bson filter = eq("id", customer.getId());
getCollection().deleteOne(filter);
}
private MongoCollection<Document> getCollection() {
return mongoClient.getDatabase("customer").getCollection("customer");
}
}
Finally, a REST Endpoint using RESTEasy Reactive and Jakarta RESTful Web Services (jakarta.ws.rs.*) is added to expose the Service:
package com.mastertheboss.rest;
import com.mastertheboss.model.Customer;
import com.mastertheboss.service.CustomerService;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
@Path("/customer")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CustomerEndpoint {
@Inject
CustomerService service;
@GET
public List<Customer> list() {
return service.list();
}
@POST
public List<Customer> add(Customer customer) {
service.add(customer);
return list();
}
@PUT
public List<Customer> put(Customer customer) {
service.update(customer);
return list();
}
@DELETE
public List<Customer> delete(Customer customer) {
service.delete(customer);
return list();
}
}
The application configuration in src/main/resources/application.properties configures the MongoDB connection string:
quarkus.mongodb.connection-string = mongodb://localhost:27017
To compile the application in Quarkus 3.x, we include RESTEasy Reactive Jackson and the Quarkus MongoDB Client extensions in our pom.xml:
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Run the application in Quarkus Development mode:
mvn quarkus:dev
Then, you can test adding a new Customer via cURL:
curl -d '{"id":1, "name":"Frank"}' -H "Content-Type: application/json" -X POST http://localhost:8080/customer
Checking the list of customers:
curl http://localhost:8080/customer
[{"id":1,"name":"Frank"}]
Updating a Customer:
curl -d '{"id":1, "name":"John"}' -H "Content-Type: application/json" -X PUT http://localhost:8080/customer
And finally deleting it:
curl -d '{"id":1, "name":"John"}' -H "Content-Type: application/json" -X DELETE http://localhost:8080/customer
Using MongoDB with Hibernate Panache
The MongoDB with Panache extension simplifies the management of database operations using the Active Record pattern. By extending PanacheMongoEntity, your entities automatically obtain default MongoDB ID generation (using ObjectId) and utility helper methods for querying and persistence.
Here is the updated Customer class using Panache Mongo with standard public field access or standard getters/setters:
package com.mastertheboss.panache;
import io.quarkus.mongodb.panache.PanacheMongoEntity;
import io.quarkus.mongodb.panache.common.MongoEntity;
import org.bson.codecs.pojo.annotations.BsonProperty;
@MongoEntity(collection = "customers")
public class Customer extends PanacheMongoEntity {
@BsonProperty("customer_name")
public String name;
public static Customer findByName(String name) {
return find("name", name).firstResult();
}
}
The @MongoEntity annotation specifies the collection name in MongoDB. The @BsonProperty annotation maps Java fields to target key names inside MongoDB documents. Extending PanacheMongoEntity gives us access to built-in helper methods such as persist(), listAll(), and findById(). Our REST Resource requires minimal boilerplate:
package com.mastertheboss.panache;
import org.bson.types.ObjectId;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;
@Path("/panache/customer")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PanacheCustomerEndpoint {
@GET
public List<Customer> list() {
return Customer.listAll();
}
@POST
public Response create(Customer customer) {
customer.persist();
return Response.status(Response.Status.CREATED).entity(customer).build();
}
@PUT
public void update(Customer customer) {
customer.update();
}
@DELETE
@Path("/{id}")
public Response delete(@PathParam("id") String id) {
Customer customer = Customer.findById(new ObjectId(id));
if (customer != null) {
customer.delete();
return Response.noContent().build();
}
return Response.status(Response.Status.NOT_FOUND).build();
}
}
Configure MongoDB connection details and database name in application.properties:
quarkus.mongodb.connection-string = mongodb://localhost:27017
quarkus.mongodb.database = customers
To use MongoDB with Panache, ensure the following extension is present in your pom.xml:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-panache</artifactId>
</dependency>
Using PanacheQuery to fetch your data
The PanacheQuery interface provides sophisticated features such as pagination, dynamic sorting, counting results, and operating directly with standard Java Streams.
Here is an example demonstrating basic pagination:
import io.quarkus.mongodb.panache.PanacheQuery;
import io.quarkus.panache.common.Page;
@Path("/page1")
@GET
public List<Customer> pageList() {
PanacheQuery<Customer> customers = Customer.findAll();
customers.page(Page.ofSize(20));
return customers.list();
}
The above endpoint fetches the first page containing up to 20 customer documents. Fetching subsequent pages is straightforward:
List<Customer> nextPage = customers.nextPage().list();
Queries can also be parameterized with field parameters using find(query, params):
@Path("/search")
@GET
public List<Customer> searchByName(@QueryParam("name") String name) {
PanacheQuery<Customer> customers = Customer.find("name", name);
customers.page(Page.ofSize(20));
return customers.list();
}
Finally, total record metadata can be obtained directly from PanacheQuery without requesting all data entities into memory:
long totalCount = customers.count();
If you want an example of Hibernate Panache with a Relational Database, check out the following article: Managing Data Persistence with Quarkus and Hibernate Panache.
You can find the source code for both examples on Github at: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/mongodb
Frequently Asked Questions (FAQs)
1. How do I migrate Quarkus MongoDB applications from Java EE to Jakarta EE in Quarkus 3.x?
In Quarkus 3.x, replace all classic Java EE imports (e.g., javax.enterprise.context.ApplicationScoped, javax.inject.Inject, and javax.ws.rs.*) with their corresponding Jakarta EE replacements starting with jakarta.* (e.g., jakarta.enterprise.context.ApplicationScoped, jakarta.inject.Inject, and jakarta.ws.rs.*).
2. What is the difference between standard RESTEasy and RESTEasy Reactive in Quarkus 3.x?
RESTEasy Reactive is the modern, non-blocking REST engine recommended for Quarkus 3.x. It leverages underlying Eclipse Vert.x event loops to execute non-blocking operations efficiently while fully supporting classic blocking imperative patterns when database interaction requires sync calls.
3. Should I use low-level MongoDB Java Client or MongoDB Panache?
Use the low-level MongoDB Java Client if you need direct control over dynamic Mongo BSON queries, aggregation pipelines, or complex custom drivers. Choose MongoDB with Panache for standard web applications where active record patterns, simplified queries, auto-generated IDs, and built-in pagination save development overhead.
Recommended Articles
Create a Quarkus REST CRUD Application with Hibernate Panache and REST Data Panache - Tutorial
Learn how to create a robust REST CRUD application in Quarkus using Hibernate Panache and REST Data Panache. #Quarkus #HibernatePanache #RESTDataPanache
Quarkus REST Client API Tutorial: MicroProfile REST Client Implementation
Learn how to develop Quarkus REST Client using MicroProfile REST Client with a basic example and configuration.
Simplify Data Persistence with Quarkus and Hibernate ORM Panache
Learn how to streamline data persistence in Quarkus projects using Hibernate ORM Panache for Java developers.
Query Quarkus REST Service with Ajax and jQuery
Learn how to create an Ajax front-end to a Quarkus REST application using jQuery and query a sample REST service running on Quarkus 0.16.1