Views
The views block inside an entity defines HTML pages and their routes. Each view binds a query to a template, producing a dynamic HTML response.
entity "User" {
views {
template "/user" query="user_by_id" html="
<h1>{{data.name}}</h1>
<p>Email: {{data.email}}</p>
"
}
}
A view becomes an HTTP endpoint at the specified route. When the route is requested, Terranova executes the associated query and renders the template with the query result as the data context.
Syntax
template "<route>" [query="<query_name>"] [entity="<entity_name>"] [file="<path>"] [html="<inline_html>"]
Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
route (name) |
string | ✓ | URL path (e.g., "/user"). The route is static; parameters are passed via query string (for GET/DELETE) or request body (for POST/PUT). |
query |
string | Name of a query (raw or composed) defined in the same entity or in another entity (see entity). If omitted, the template is static (no data binding). |
|
entity |
string | Name of the entity where the query is defined. Required when the query belongs to a different entity. | |
file |
string | Path to an external HTML template file. Mutually exclusive with html. |
|
html |
string | Inline HTML template string. Mutually exclusive with file. |
One of file or html must be present.
Note:
template_setis not yet supported. Only individualtemplatenodes are available.
Data Context for Templates
The data available in a template depends on the type of query used.
Raw Query
When a view uses a raw query, the query result is an array of rows. This array is placed under a top‑level key named data. Each row is an object with fields corresponding to the SQL result columns.
get "user_by_id" sql="SELECT id, name, email FROM user WHERE id = :id" {
param "id" type="int"
}
views {
template "/user" query="user_by_id" html="
<h1>{{data.name}}</h1>
<p>Email: {{data.email}}</p>
"
}
When a client requests GET /user?id=123, the query receives :id = 123. Because the query returns a single row (by primary key), the data array contains one element. Access its fields using {{data.name}}.
For queries that return multiple rows, use a section to iterate:
get "all_users" sql="SELECT id, name FROM user"
views {
template "/users" query="all_users" html="
<ul>
{{#data}}
<li>{{name}}</li>
{{/data}}
</ul>
"
}
Composed Query
When a view uses a composed query, the result is an object where each key corresponds to a data block in the composed query. Each of those values is itself an object containing a data array (the sub‑query’s rows), plus count, error, and modified.
get "user_with_posts" {
param "id" type="int"
data "user" query="user_by_id" { bind "id" }
data "posts" query="posts_by_user" { bind "user_id" from="id" }
}
views {
template "/user_posts" query="user_with_posts" html="
<h1>{{user.data.name}}</h1>
<h2>Posts</h2>
<ul>
{{#posts.data}}
<li>{{title}}</li>
{{/posts.data}}
</ul>
"
}
In this example:
-
user.datais an array of user rows (one element). -
posts.datais an array of post rows.
Static Template (No Query)
If no query is specified, the template receives no data context. It is rendered as a static HTML page.
Static templates are useful for informational pages, documentation, or forms that submit to API endpoints.
Parameter Binding
Parameters are passed to the query via:
-
URL query string for
GETandDELETErequests. -
JSON request body for
POSTandPUTrequests.
The query parameter names must match the names declared in the query's param nodes.
Example:
get "user_by_id" sql="SELECT * FROM user WHERE id = :id" {
param "id" type="int"
}
views {
template "/user" query="user_by_id" html="<h1>{{data.name}}</h1>"
}
A request to GET /user?id=42 executes the query with :id = 42.
Note: Path parameters (e.g.,
/user/:id) are not supported for views. Always use query string parameters for dynamic values.
Using Queries from Another Entity
To use a query defined in a different entity, specify the entity attribute:
entity "User" {
queries {
get "profile" sql="SELECT * FROM user WHERE id = :id" {
param "id" type="int"
}
}
}
entity "Admin" {
views {
template "/admin/user" query="profile" entity="User" html="
<h1>{{data.name}}</h1>
"
}
}
External Template Files
For larger templates, use the file attribute to reference an external HTML file:
The file path is relative to the location of the .kdl specification file. Terranova watches these files for changes in development mode.
Template Syntax
Terranova implements a subset of Mustache. All features support dotted paths (nested variable access).
| Feature | Syntax | Example | Notes |
|---|---|---|---|
| Interpolation | {{ variable }} |
{{user.data.name}} |
Dotted sequences (e.g., {{user.address.city}}) are supported. |
| Sections (iteration / truthy) | {{#variable}} ... {{/variable}} |
{{#user.posts}} <li>{{title}}</li> {{/user.posts}} |
Dotted paths are supported. The section iterates over the nested array or object. |
| Inverted sections (falsey / empty) | {{^variable}} ... {{/variable}} |
{{^user.posts}} <p>No posts.</p> {{/user.posts}} |
Dotted paths are supported. |
| Comments | {{! comment }} |
{{! This will not appear in output }} |
|
| Delimiter change | {{=<% %>=}} |
{{=<% %>=}}<% name %> |
Allows changing the tag delimiters. |
Complete Example
application "Blog" version="0.1" {
entity "User" {
schema {
pk "id" type="int"
field "name" type="string"
field "email" type="string"
}
queries {
get "user_by_id" sql="SELECT * FROM user WHERE id = :id" {
param "id" type="int"
}
get "all_users" sql="SELECT id, name FROM user"
}
views {
template "/user" query="user_by_id" html="
<!DOCTYPE html>
<html>
<head><title>{{data.name}}</title></head>
<body>
<h1>{{data.name}}</h1>
<p>Email: {{data.email}}</p>
</body>
</html>
"
template "/users" query="all_users" html="
<ul>
{{#data}}
<li><a href='/user?id={{id}}'>{{name}}</a></li>
{{/data}}
{{^data}}
<li>No users registered yet.</li>
{{/data}}
</ul>
"
template "/about" html="<h1>About this Blog</h1>"
}
}
profile "dev" default=true {
listen address="127.0.0.1" port=8080
}
}
What Terranova Does With Views
- Route registration – Each
templatecreates an HTTP endpoint at the specified static route. - Query execution – If
queryis provided, the query runs before rendering, and its result becomes the template context. - Parameter binding – Parameters are taken from the request's query string (for
GET/DELETE) or JSON body (forPOST/PUT). Path parameters are not supported. - Data context structure:
- Raw query → context contains a top‑level
dataarray. - Composed query → context contains keys named after each
datablock, each with adataarray (andcount,error,modified). - Template rendering – The template (inline or external) is rendered with the query result using a subset of Mustache:
- Interpolation supports dotted paths.
- Sections and inverted sections support dotted paths.
- Comments and delimiter changes are supported.
- Static views – Views without a query return the template as‑is, with no data binding.