Reactive systems are becoming essential for modern backend applications that need to handle large numbers of concurrent requests efficiently. Traditional blocking applications often struggle under heavy load because each request consumes a dedicated thread.
With Quarkus and Mutiny, building highly scalable reactive microservices in Java becomes significantly easier.
In this tutorial, we will build a reactive product microservice using Quarkus and Mutiny. The service will expose REST endpoints that return asynchronous responses using Uni and Multi.
By the end of this tutorial, you will learn how to:
- Create a Quarkus reactive microservice
- Use Mutiny’s
UniandMulti - Build non-blocking REST APIs
- Handle asynchronous transformations
- Simulate reactive data streams
- Test reactive endpoints
Let’s get started.
What You Will Learn
In this tutorial, you’ll build:
- A Product Service
- Reactive CRUD-style endpoints
- Streaming endpoint for real-time product updates
Technologies used:
- Java 21
- Quarkus 3.x
- Mutiny
- RESTEasy Reactive
- Maven
Prerequisites
Before starting, make sure you have:
- Java 21 installed
- Maven installed
- VS Code or IntelliJ IDEA
- Basic knowledge of Java REST APIs
Check installed versions:
java -version
java version "21.0.8" 2025-07-15 LTS
Java(TM) SE Runtime Environment (build 21.0.8+12-LTS-250)
Java HotSpot(TM) 64-Bit Server VM (build 21.0.8+12-LTS-250, mixed mode, sharing)
mvn -version
Apache Maven 3.9.10 (5f519b97e944483d878815739f519b2eade0a91d)
Maven home: /Users/didin/.sdkman/candidates/maven/current
Java version: 21.0.8, vendor: Oracle Corporation, runtime: /Users/didin/.sdkman/candidates/java/21.0.8-oracle
Default locale: en_ID, platform encoding: UTF-8
OS name: "mac os x", version: "26.3.1", arch: "aarch64", family: "mac"
Step 1: Create a New Quarkus Project
Use the Quarkus CLI:
quarkus create app com.djamware:quarkus-reactive-microservice \
--extension='resteasy-reactive,jackson'
Or using Maven:
mvn io.quarkus.platform:quarkus-maven-plugin:3.34.3:create \
-DprojectGroupId=com.djamware \
-DprojectArtifactId=quarkus-reactive-microservice \
-DclassName="com.djamware.ProductResource" \
-Dpath="/products"
Move into the project:
cd quarkus-reactive-microservice
Step 2: Project Structure
The project structure will look like this:
src
└── main
└── java
└── com
└── djamware
├── Product.java
├── ProductService.java
└── ProductResource.java
Step 3: Create the Product Model
Create Product.java
package com.djamware;
public class Product {
public Long id;
public String name;
public Double price;
public Product() {
this.id = null;
this.name = null;
this.price = null;
}
public Product(Long id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
}
Step 4: Create a Reactive Service with Mutiny
Now create ProductService.java
package com.djamware;
import java.time.Duration;
import java.util.List;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ProductService {
private final List<Product> products = List.of(
new Product(1L, "Laptop", 1200.0),
new Product(2L, "Mouse", 25.0),
new Product(3L, "Keyboard", 75.0));
public Uni<List<Product>> getAllProducts() {
return Uni.createFrom().item(products);
}
public Uni<Product> getProductById(Long id) {
return Uni.createFrom()
.item(products.stream()
.filter(p -> p.id.equals(id))
.findFirst()
.orElse(null));
}
public Multi<Product> streamProducts() {
return Multi.createFrom()
.iterable(products)
.onItem().call(product -> Uni.createFrom()
.nullItem()
.onItem().delayIt().by(Duration.ofSeconds(1)));
}
}
Step 5: Create REST Resource
Now create ProductResource.java
package com.djamware;
import java.util.List;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/products")
@Produces(MediaType.APPLICATION_JSON)
public class ProductResource {
ProductService productService;
@Inject
public ProductResource(ProductService productService) {
this.productService = productService;
}
@GET
public Uni<List<Product>> getAll() {
return productService.getAllProducts();
}
@GET
@Path("/{id}")
public Uni<Product> getById(@PathParam("id") Long id) {
return productService.getProductById(id);
}
@GET
@Path("/stream")
@Produces(MediaType.SERVER_SENT_EVENTS)
public Multi<Product> stream() {
return productService.streamProducts();
}
}
Step 6: Understanding Uni vs Multi
This is the most important part of Mutiny.
Uni
Uni represents one asynchronous item.
Examples:
- fetch one record
- API response
- database query result
Uni<Product>
Think of it as:
Promise / Future for one result
Step 7: Understanding Multi
Multi represents a stream of items.
Examples:
- event streams
- message queues
- live notifications
- SSE streaming
Multi<Product>
Think of it as:
asynchronous stream / observable sequence
Step 8: Add Reactive Transformation
Let’s add a price discount transformation.
Update service:
public Uni<Product> getDiscountedProduct(Long id) {
return getProductById(id)
.onItem().transform(product -> {
product.price = product.price * 0.9;
return product;
});
}
This is a clean example of the Mutiny transformation.
Step 9: Run the Application
Start development mode:
./mvnw quarkus:dev
Test endpoints:
curl http://localhost:8080/products
curl http://localhost:8080/products/1
Streaming:
curl http://localhost:8080/products/stream
Step 10: Example Output
[
{
"id": 1,
"name": "Laptop",
"price": 1200.0
},
{
"id": 2,
"name": "Mouse",
"price": 25.0
}
]
Why Use Reactive Microservices?
Reactive microservices help with:
- better scalability
- Reduced thread blocking
- efficient resource usage
- high concurrency
- cloud-native workloads
This is especially useful for:
- API gateways
- event-driven services
- streaming systems
- real-time dashboards
Conclusion
In this tutorial, you learned how to build a reactive microservice using Quarkus and Mutiny.
You now understand:
UniMulti- reactive REST endpoints
- SSE streaming
- asynchronous transformation
This foundation is excellent for building production-ready cloud-native Java microservices.
You can find the full source code on our GitHub.
You can find my first Ebook about Angular 21 + Spring Book 4 JWT Authentication here
We know that building beautifully designed Mobile and Web Apps from scratch can be frustrating and very time-consuming. Check Envato unlimited downloads and save development and design time.
That's just the basics. If you need more deep learning about Quarkus and Microservices, you can take the following cheap course:
- Cloud-native Microservices with Quarkus
- Building Microservices with Quarkus
- (2025) Quarkus for beginners, everything you need to know.
- Accessing Relational Databases with Quarkus
- Learn Vert.x - Reactive microservices with Java
- Quarkus - Simple REST API and Unit Tests with JUnit 5
- The complete guide to running Java in Docker and Kubernetes
- The Complete Microservices With Java
Thanks!
