Template Parameter Declarations

If you declare a parameter declaration in a template, Qute will attempt to validate all expressions that reference this parameter. If an incorrect expression is found the build will fail. This can greatly reduce developer errors upfront. Let's exercise this.

Create a new class at src/main/java/org/acme/Item.java and enter in the body:
package org.acme;

import java.math.BigDecimal;

public class Item {
    public String name;
    public BigDecimal price;

    public Item(BigDecimal price, String name) {
        this.price = price;
        this.name = name;
    }
}

Creating the Template

Now, suppose we want to render a simple HTML page that contains the item name and price. First, create a directory from the root directory of the project:
mkdir -p src/main/resources/templates/ItemResource

Create a Qute template in that directory named  item.html and enter the following body:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>{item.name}</title>
    </head>
    <body>
        <h1>{item.name}</h1>
        <div>Price: {item.price}</div>
    </body>
</html>

Creating the Service

The next step is to create a simple service to mock up a database of items. Create a new file at src/main/java/org/acme/ItemService.java and enter the following code: 

package org.acme;

import java.math.BigDecimal;
import java.util.Map;
import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class ItemService {

    private Map<Integer, Item> items = Map.of(
        1, new Item(new BigDecimal(1.99), "Apple"),
        2, new Item(new BigDecimal(2.99), "Pear"),
        3, new Item(new BigDecimal(3.99), "Grape"),
        4, new Item(new BigDecimal(129.99), "Mango")
    );

    public Item findItem(int id) {
        return items.get(id);
    }
}

Creating the REST Endpoint

The last step is to create the REST endpoint to access the service. This implementation will use a type-safe template. Create a new file at src/main/java/org/acme/qute/ItemResource.java and enter the following:

package org.acme;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.CheckedTemplate;

@Path("item")
public class ItemResource {

    @Inject
    ItemService service;

    @CheckedTemplate
    public static class Templates {
        public static native TemplateInstance item(Item item);
    }

    @GET
    @Path("{id}")
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance get(@PathParam("id") Integer id) {
        return Templates.item(service.findItem(id));
    }
}
In this implementation, a static Templates inner class is created with a method called item(). That method provides a TemplateInstance for templates/ItemResource/item.html and declare its Item item parameter so Quarkus can validate the template.
The value of item is passed in the endpoint as id. This parameter is used to render the template when the REST endpoint is called. You can test this endpoint out by running:
curl http://localhost:8080/item/1

You should see an HTML result that shows "Apple" and its price:
 

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>Apple</title>
    </head>
    <body>
        <h1>Apple</h1>
        <div>Price: 1.9899999999999999911182158029987476766109466552734375</div>
    </body>
</html>
You can also see an actual HTML rendering in your browser using the same URL.

Template Parameter Declaration Inside the Template

Alternatively, to declare that a template is expecting an Item type, you can declare it in the template file itself by adding an additional type and parameter name. This simplifies the Java code while still maintaining type checking.
 

Update the src/main/resources/templates/ItemResource/item.html file and replace the existing contents with the following body:
{@org.acme.Item item}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{item.name}</title>
</head>
<body>
    <h1>{item.name}</h1>
    <div>Price: {item.price}</div>
</body>
</html>

Notice the first line, which is an optional parameter declaration. If declared, Qute attempts to validate all expressions that reference the parameter item.
 

Now the ItemResource class needs to be updated (and simplified) to let the type checking occur by the template itself. Open the src/main/java/org/acme/ItemResource.java file and replace its existing contents with:
package org.acme;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.Location;
import io.quarkus.qute.Template;

@Path("item")
public class ItemResource {

    @Inject
    ItemService service;

    @Inject
    @Location("ItemResource/item")
    Template item;

    @GET
    @Path("{id}")
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance get(@PathParam("id") Integer id) {
        return item.data("item", service.findItem(id));
    }
}
Since the code is functionally the same, running the same test as before should produce the same result:
curl http://localhost:8080/item/1

If you made any errors, you'll see them immediately in the rendered output (the Live Coding rebuild will fail).
 

If you did not see errors, congratulations! But let's see what happens if you did. Open the src/main/resources/templates/ItemResource/item.html file and change the parameter look up on line 6 from:
{item.name}
to
{item.nonsense}
Exercise the bug by accessing the REST endpoint in a web browser:
http://localhost:8080/item/1
You should see an error on the web page:
Image
Error page
Qute checks the syntax and will fail the build (and result in a prettified HTML error screen if accessed through a browser) when syntax errors are detected in the template. This makes it very easy to quickly iterate, update code and template, and see the results.

Before Moving On

Be sure to change the value back to {item.name}! You won't need to restart the Quarkus process; simply access the REST endpoint again and Quarkus will fix itself.
Daniel Oh
Daniel Oh
Senior Principal Developer Advocate
Daniel Oh is a Senior Principal Developer Advocate at Red Hat. He works to evangelize building cloud-native microservices and serverless functions with cloud-native runtimes to developers. He also continues to contribute to various open-source cloud projects and ecosystems as a Cloud Native Computing Foundation (CNCF) ambassador for accelerating DevOps adoption in enterprises. Daniel also speaks at technical seminars, workshops, and meetups to elaborate on new emerging technologies for enterprise developers, SREs, platform engineers, and DevOps teams.