This tutorial explains the differences between Quarkus RESTEasy Classic and RESTEasy Reactive. You’ll learn how to choose between them and which one fits best for your new applications.
Background
Since the beginning, Quarkus provided support for REST endpoints through Quarkus RESTEasy Classic. However, new workloads should consider Quarkus REST (formerly RESTEasy Reactive), which supports both traditional blocking and fully reactive workloads. That being said, let’s see how both approaches work and which dependencies you should use in either case.
RESTEasy Classic
Let’s begin with the dependencies. In order to use RESTEasy Classic, the following APIs are available:

Within your pom.xml, it translates into:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
Let’s check a simple endpoint which uses the classic API.
RESTEasy Classic Sample Endpoint
@Path("/books")
public class BookResource {
private List<Book> books = Collections.synchronizedList(new ArrayList<>());
public BookResource() {
books.add(new Book("1984", "George Orwell"));
books.add(new Book("Brave New World", "Aldous Huxley"));
}
@GET
public List<Book> list() {
return books;
}
@POST
public List<Book> add(Book book) {
books.add(book);
return books;
}
@DELETE
public List<Book> delete(Book book) {
books.removeIf(existingBook -> existingBook.title.contentEquals(book.title));
return books;
}
}
If general terms, if you’re migrating a legacy JBoss/WildFly app, REST Classic is usually the easiest drop-in. It works quite well with @Transactional,
CDI-based services. These applications are usually following a synchronous request-response pattern. For example they request an information from the DB and they return the response when it’s ready. Besides, with RESTEasy Classic you can still put in place AsyncResponses to submit the request through a separate thread of execution:
Async Support (with Classic)
private final ExecutorService executor = Executors.newFixedThreadPool(4);
@GET
public void asyncMethod(@Suspended AsyncResponse asyncResponse) {
executor.submit(() -> {
try {
Thread.sleep(1000);
asyncResponse.resume("Async response!");
} catch (InterruptedException e) {
asyncResponse.resume(e);
}
});
}
RESTEasy Classic is ideal for legacy synchronous apps. However, as you start to consider also other aspects such as backpressure, cancellation or cooperative scheduling, using Async patterns with RESTEasy Classic can be cumbersome and error prone.
RESTEasy Reactive
Firstly, let’s check the code dependency you need to add:

In terms of pom.xml:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
</dependency>
Then, let’s check a sample REST Reactive Endpoint which uses this API.
Sample Reactive Endpoint
@Path("/hello")
public class ReactiveGreetingResource {
@Inject
ReactiveGreetingService service;
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("/greeting/{name}")
public Uni<String> greeting(String name) {
return service.greeting(name);
}
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("/greeting/{count}/{name}")
public Multi<String> greetings(int count, String name) {
return service.greetings(count, name);
}
@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
@RestSseElementType(MediaType.TEXT_PLAIN)
@Path("/stream/{count}/{name}")
public Multi<String> greetingsAsStream(int count, String name) {
return service.greetings(count, name);
}
@GET
@Produces(MediaType.TEXT_PLAIN)
@NonBlocking
public String hello() {
return "hello";
}
}
Key APIs
Uni<String>: Emits a single asynchronous value (likeCompletionStage). Non-blocking and efficient.Multi<String>: Emits a stream of values. Useful for batch or streaming responses.- SSE Streaming: Native support using
@Produces(SERVER_SENT_EVENTS)and@RestSseElementType. @NonBlocking: Tells RESTEasy Reactive to use event-loop thread — cannot be used with Classic.
Conclusion
In this tutorial, we explore the key differences between RESTEasy Classic and RESTEasy Reactive, the two main REST frameworks available in Quarkus. We compare their programming models, threading behavior, and support for asynchronous and reactive features. Through practical examples, you’ll learn how:
RESTEasy Classic uses traditional servlet-based, thread-per-request handling with optional async support via AsyncResponse.
RESTEasy Reactive embraces non-blocking I/O, Uni/Multi types from Mutiny, and event-loop based execution for better scalability.
Features like streaming, backpressure, and SSE (Server-Sent Events) are much easier to implement in the reactive model.
Keep reading this article about Quarkus Reactive applications: Quarkus Reactive REST made easy