Other examples
Flash messages — every type
The success!, info!, warning!, error! macros store the message in the session: it shows on the next page, so after a redirect.
pub async fn demo_messages(request: Request) -> AppResult<Response> {
success!(request.notices => "This is a success message.");
info!(request.notices => "This is an information message.");
warning!(request.notices => "This is a warning message.");
error!(request.notices => "This is an error message.");
Ok(Redirect::to("/").into_response())
}
For a message on the page the same request renders (a form shown again after an error), use flash_now!:
context_update!(request => {
"messages" => flash_now!(warning => "Check the fields."),
});
request.render("page.html")
{% messages %} shows the context's messages key: the session messages that came with the request, or flash_now!'s if you replace it. Replaced, it loses this request's session messages.
REST API
API routes
pub fn routes() -> Routes {
urlpatterns! {
"/api/articles" => view!{ api_list_articles }, name = "api_articles",
}
}
JSON API handler
use runique::prelude::*;
use runique::prelude::serde_json::json;
pub async fn api_list_articles(request: Request) -> AppResult<Response> {
let articles = article::Entity::find()
.all(&*request.engine.db)
.await?;
Ok(Json(json!({
"count": articles.len(),
"data": articles
})).into_response())
}
data serializes every column of the model. Never do it on a table holding a secret (the accounts: password hash): select the columns to expose, or go through a dedicated struct.
A JSON POST is refused with a 403 by the CSRF middleware without a valid X-CSRF-Token header. An HTML form POST that the view handles without request.form (sign-out, an action) must check request.prisme.csrf_valid itself (see Forms).
Complete base template
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}My App{% endblock %}</title>
<link rel="stylesheet" href='{% static "css/main.css" %}'>
{% block extra_css %}{% endblock %}
</head>
<body>
<header>
<nav>
<a href='{% link "index" %}'>Home</a>
<a href='{% link "about" %}'>About</a>
<a href='{% link "signup" %}'>Sign up</a>
</nav>
</header>
{% messages %}
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2026 — Powered by Runique</p>
</footer>
{% block extra_js %}{% endblock %}
</body>
</html>
Pattern summary
| Pattern | When to use |
|---|---|
request.render("template.html") | Standard HTML rendering |
Redirect::to("/").into_response() | After a successful action (POST) |
context_update!(request => {...}) | Inject variables into the template |
success!(request.notices => "...") | Flash message before redirect |
flash_now!(error => "...") | Immediate message (no redirect) |
ValidationForm::try_form(form, &request).await | Validate a form (only when it was sent) |
validated.save(&db).await | Persist to the database (not for an account: see Forms) |
form.forms_mut().database_error(&err) | Display a DB error inside the form |