Scenario 1: Simple Synchronous Operation

@GET
@Produces(MediaType.TEXT_PLAIN)
public String greeting() {
    return "Hello!";
}

Use Case: You have a straightforward operation that completes quickly and doesn’t involve any asynchronous tasks (database queries, network calls, etc.).

Why No Uni: In this case, using Uni would introduce unnecessary complexity. You can simply return the result directly as a String.

Scenario 2: Asynchronous Operation with a Single Result

@GET
@Produces(MediaType.APPLICATION_JSON)
public Uni<MyData> getData() {
    return Uni.createFrom().emitter(e -> {
        // Asynchronous task to fetch MyData (e.g., database query)
        e.complete(result); // Emit the result
        // Or e.fail(error); // Emit an error if something goes wrong
    });
}

Use Case: Your operation involves an asynchronous task that will eventually produce a single result (MyData in this example).

Why Uni: Uni is perfect for this scenario. It represents the asynchronous result, allowing you to:

  • Handle the result when it becomes available.
  • Handle potential errors gracefully.
  • Compose with other asynchronous operations.

Scenario 3: Asynchronous Operation with Multiple Results

@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
public Multi<String> getUpdates() {
    return Multi.createFrom().ticks().every(Duration.ofSeconds(1))
               .onItem().transform(n -> "Update #" + n);
}

Use Case: Your operation produces a continuous stream of results (e.g., real-time updates).

Why Multi (Not Uni): Multi is designed for streams of data. It allows you to emit multiple values over time and have the client receive them as they become available.

Scenario 4: Fire-and-Forget Operation

@POST
@Consumes(MediaType.APPLICATION_JSON)
public Uni<Void> processData(MyData data) {
    return Uni.createFrom().voidItem()
              .onItem().invoke(() -> {
                  // Asynchronous processing of the data (e.g., sending an email)
              });
}

Use Case: You initiate an asynchronous task but don’t need to return a specific result to the client.

Why Uni: Returning Uni signals that the operation has been initiated and will complete in the background. The client doesn’t need to wait for a specific result.

Key Points:

  • Synchronous: No asynchronous tasks? Return the result directly (e.g., String, MyData).
  • Single Asynchronous Result: Use Uni to represent the result and handle it when it becomes available.
  • Multiple Asynchronous Results: Use Multi to emit a stream of data.
  • Fire and Forget: Use Uni when you don’t need to return a specific value.