or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/io.quarkus/quarkus-resteasy-reactive@3.15.x

docs

index.md
tile.json

tessl/maven-io-quarkus--quarkus-resteasy-reactive

tessl install tessl/maven-io-quarkus--quarkus-resteasy-reactive@3.15.0

A Jakarta REST implementation utilizing build time processing and Vert.x for high-performance REST endpoints with reactive capabilities in cloud-native environments.

qute-templates.mddocs/reference/

Qute Template Integration

Quarkus REST integrates with Qute, a type-safe template engine, enabling server-side HTML rendering and template-driven responses from REST endpoints.

Import

import io.quarkus.resteasy.reactive.qute.RestTemplate;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.Template;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

RestTemplate

Utility class for creating template instances with data for REST endpoint responses.

public final class RestTemplate {
    /**
     * Create a template instance with a single data entry
     * @param name Data key name
     * @param value Data value
     * @return TemplateInstance ready for rendering
     */
    public static TemplateInstance data(String name, Object value);

    /**
     * Create a template instance with the data object as root
     * @param data Data object
     * @return TemplateInstance ready for rendering
     */
    public static TemplateInstance data(Object data);
}

Basic Usage

Simple Template Rendering

@Path("/hello")
public class HelloResource {

    @GET
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance hello(@RestQuery String name) {
        return RestTemplate.data("name", name != null ? name : "World");
    }
}

Template file (src/main/resources/templates/HelloResource/hello.html):

<!DOCTYPE html>
<html>
<head>
    <title>Hello</title>
</head>
<body>
    <h1>Hello {name}!</h1>
</body>
</html>

Multiple Data Values

@GET
@Path("/user/{id}")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance userProfile(@RestPath String id) {
    User user = userService.findById(id);
    List<Order> orders = orderService.findByUserId(id);

    return RestTemplate.data("user", user)
        .data("orders", orders)
        .data("pageTitle", "User Profile");
}

Template file (src/main/resources/templates/UserResource/userProfile.html):

<!DOCTYPE html>
<html>
<head>
    <title>{pageTitle}</title>
</head>
<body>
    <h1>{user.name}</h1>
    <p>Email: {user.email}</p>

    <h2>Orders</h2>
    <ul>
    {#for order in orders}
        <li>{order.id} - {order.total}</li>
    {/for}
    </ul>
</body>
</html>

Single Data Object

@GET
@Path("/product/{id}")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance product(@RestPath String id) {
    Product product = productService.findById(id);
    return RestTemplate.data(product);
}

Template file (src/main/resources/templates/ProductResource/product.html):

<!DOCTYPE html>
<html>
<head>
    <title>{name}</title>
</head>
<body>
    <h1>{name}</h1>
    <p>{description}</p>
    <p>Price: ${price}</p>
    <p>In Stock: {stock}</p>
</body>
</html>

Template Location

Templates are resolved by convention based on the resource class and method name:

src/main/resources/templates/{ClassName}/{methodName}.html

Examples:

  • HelloResource.hello()templates/HelloResource/hello.html
  • UserResource.profile()templates/UserResource/profile.html
  • ProductResource.details()templates/ProductResource/details.html

Custom Template Names

Use @io.quarkus.qute.CheckedTemplate for type-safe templates:

@Path("/items")
public class ItemResource {

    @CheckedTemplate
    public static class Templates {
        public static native TemplateInstance item(Item item);
        public static native TemplateInstance list(List<Item> items);
    }

    @GET
    @Path("/{id}")
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance getItem(@RestPath String id) {
        Item item = itemService.findById(id);
        return Templates.item(item);
    }

    @GET
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance listItems() {
        List<Item> items = itemService.findAll();
        return Templates.list(items);
    }
}

Templates:

  • templates/ItemResource/item.html
  • templates/ItemResource/list.html

Template Features

Conditional Rendering

<body>
    <h1>{user.name}</h1>
    {#if user.isPremium}
        <span class="badge">Premium Member</span>
    {/if}

    {#if user.orders.size() > 0}
        <h2>Recent Orders</h2>
        <ul>
        {#for order in user.orders}
            <li>{order.id}</li>
        {/for}
        </ul>
    {#else}
        <p>No orders yet</p>
    {/if}
</body>

Loops and Iteration

<h2>Products</h2>
<table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Price</th>
            <th>Stock</th>
        </tr>
    </thead>
    <tbody>
    {#for product in products}
        <tr>
            <td>{product.name}</td>
            <td>${product.price}</td>
            <td>{product.stock}</td>
        </tr>
    {/for}
    </tbody>
</table>

Nested Data Access

<div class="user-card">
    <h3>{user.name}</h3>
    <p>{user.email}</p>

    <h4>Address</h4>
    <p>{user.address.street}</p>
    <p>{user.address.city}, {user.address.state} {user.address.zip}</p>

    <h4>Company</h4>
    <p>{user.company.name}</p>
    <p>{user.company.website}</p>
</div>

Expressions and Operators

<p>Total: ${order.subtotal + order.tax + order.shipping}</p>
<p>Discount: {order.discount}%</p>
<p>Final: ${order.total * (1 - order.discount / 100)}</p>

{#if user.age >= 18}
    <p>Adult</p>
{#else}
    <p>Minor</p>
{/if}

Form Handling

Form Display

@GET
@Path("/create")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance createForm() {
    return RestTemplate.data("item", new Item());
}

@POST
@Path("/create")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_HTML)
public TemplateInstance createSubmit(
    @FormParam("name") String name,
    @FormParam("description") String description,
    @FormParam("price") BigDecimal price
) {
    Item item = new Item();
    item.setName(name);
    item.setDescription(description);
    item.setPrice(price);

    itemService.create(item);

    return RestTemplate.data("item", item)
        .data("message", "Item created successfully");
}

Template (templates/ItemResource/createForm.html):

<!DOCTYPE html>
<html>
<head>
    <title>Create Item</title>
</head>
<body>
    <h1>Create New Item</h1>

    {#if message}
        <p class="success">{message}</p>
    {/if}

    <form method="post" action="/items/create">
        <label>Name:
            <input type="text" name="name" value="{item.name}" required>
        </label>

        <label>Description:
            <textarea name="description">{item.description}</textarea>
        </label>

        <label>Price:
            <input type="number" name="price" value="{item.price}" step="0.01" required>
        </label>

        <button type="submit">Create</button>
    </form>
</body>
</html>

Error Pages

Custom Error Handling

@Path("/items")
public class ItemResource {

    @GET
    @Path("/{id}")
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance getItem(@RestPath String id) {
        Item item = itemService.findById(id);

        if (item == null) {
            return RestTemplate.data("itemId", id)
                .data("error", "Item not found");
        }

        return RestTemplate.data("item", item);
    }
}

Template with error handling:

<!DOCTYPE html>
<html>
<head>
    <title>Item Details</title>
</head>
<body>
    {#if error}
        <div class="error">
            <h1>Error</h1>
            <p>{error}</p>
            <p>Item ID: {itemId}</p>
        </div>
    {#else}
        <h1>{item.name}</h1>
        <p>{item.description}</p>
        <p>Price: ${item.price}</p>
    {/if}
</body>
</html>

Template Fragments

Include Partials

@GET
@Path("/dashboard")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance dashboard() {
    return RestTemplate.data("user", currentUser())
        .data("stats", getStatistics())
        .data("notifications", getNotifications());
}

Main template (templates/DashboardResource/dashboard.html):

<!DOCTYPE html>
<html>
<head>
    <title>Dashboard</title>
</head>
<body>
    <div class="header">
        {#include header.html /}
    </div>

    <div class="content">
        <div class="stats">
            {#include stats.html /}
        </div>

        <div class="notifications">
            {#include notifications.html /}
        </div>
    </div>

    <div class="footer">
        {#include footer.html /}
    </div>
</body>
</html>

Reactive Templates

Async Data Loading

@GET
@Path("/async/{id}")
@Produces(MediaType.TEXT_HTML)
public Uni<TemplateInstance> getAsync(@RestPath String id) {
    return itemService.findByIdAsync(id)
        .map(item -> RestTemplate.data("item", item));
}

@GET
@Path("/combined")
@Produces(MediaType.TEXT_HTML)
public Uni<TemplateInstance> getCombined() {
    Uni<User> userUni = userService.getCurrentUserAsync();
    Uni<List<Order>> ordersUni = orderService.getRecentOrdersAsync();

    return Uni.combine().all()
        .unis(userUni, ordersUni)
        .asTuple()
        .map(tuple -> RestTemplate.data("user", tuple.getItem1())
            .data("orders", tuple.getItem2()));
}

Best Practices

Separate Concerns

// Good: Resource methods focus on data preparation
@GET
@Path("/{id}")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance getItem(@RestPath String id) {
    Item item = itemService.findById(id);
    List<Review> reviews = reviewService.findByItemId(id);
    double averageRating = reviewService.calculateAverage(id);

    return RestTemplate.data("item", item)
        .data("reviews", reviews)
        .data("averageRating", averageRating);
}

Use Type-Safe Templates

@CheckedTemplate
public static class Templates {
    public static native TemplateInstance item(Item item, List<Review> reviews);
}

@GET
@Path("/{id}")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance getItem(@RestPath String id) {
    // Compiler checks template exists and parameter types match
    return Templates.item(
        itemService.findById(id),
        reviewService.findByItemId(id)
    );
}

Cache Template Instances

Templates are automatically cached by Quarkus. No manual caching needed.

Content Negotiation

@GET
@Path("/{id}")
public Response getItem(@RestPath String id, @HeaderParam("Accept") String accept) {
    Item item = itemService.findById(id);

    if (accept != null && accept.contains(MediaType.TEXT_HTML)) {
        return Response.ok(RestTemplate.data("item", item))
            .type(MediaType.TEXT_HTML)
            .build();
    } else {
        return Response.ok(item)
            .type(MediaType.APPLICATION_JSON)
            .build();
    }
}

Or use @Produces with multiple media types:

@GET
@Path("/{id}")
@Produces({MediaType.TEXT_HTML, MediaType.APPLICATION_JSON})
public Object getItem(@RestPath String id) {
    Item item = itemService.findById(id);
    // Quarkus automatically selects format based on Accept header
    return item;  // JSON by default, or use RestTemplate for HTML
}

Configuration

Configure Qute templates via application.properties:

# Template suffix (default: html)
quarkus.qute.suffixes=html,txt,xml

# Template cache
quarkus.qute.dev-mode.cache=false

# Strict rendering (fail on missing properties)
quarkus.qute.strict-rendering=true

# Remove standalone lines
quarkus.qute.remove-standalone-lines=true