Advanced Askama: Custom Filters, Error Handling & Web Integration
Learning Objectives: By the end of this lesson, you will be able to:
- Write and use custom filters in Askama templates
- Handle template rendering errors gracefully
- Integrate Askama with a web framework (Axum)
1. Welcome and Introduction (5 minutes)
Icebreaker Activity
Have you ever needed to format data in a special way in your templates, or wondered how to handle errors when rendering HTML? What about connecting your templates to a real web server?
Lesson Overview
- Duration: 75-90 minutes
- Prerequisites: Day 1-3 lessons (Askama setup, control flow, reusability)
- Materials Needed:
- Rust toolchain
- Code editor
- Askama project (preferably with Axum for web integration)
2. Learning Objectives and Session Outline (5 minutes)
What We’ll Cover Today:
- Writing custom filters with
#[askama::filter_fn] - Error handling in Askama
- Integrating Askama with Axum (or another web framework)
- Hands-on practice and exercises
- Common pitfalls and troubleshooting
Success Criteria:
By the end of this session, you should be able to extend Askama with custom logic, handle errors, and serve templates in a web app.
3. Foundation Knowledge (15-20 minutes)
Core Concepts
Custom Filters
Filters let you transform data in your templates. Askama comes with many built-in filters, but you can write your own in Rust for custom formatting.
Real-World Analogy: Like using a kitchen tool to slice or dice your data before serving it on a plate (the template).
Why This Matters:
- Format data exactly as you need
- Reuse formatting logic across templates
Error Handling
Template typos fail at compile time; runtime errors can come from custom filters. The to_kebab_case filter above is infallible, so its Err branch is only defensive. See the failing shout filter example below for an actual render failure.
Askama’s render() returns a Result, so you can handle errors gracefully instead of panicking.
Web Integration
Askama integrates with web frameworks like Axum, making it easy to return rendered templates as HTTP responses.
Key Terminology
- Custom Filter: A Rust function used in templates to transform data
- Error Handling: Managing failures in rendering or data
- Integration: Connecting Askama to a web server
4. Detailed Explanation and Examples (25-30 minutes)
Understanding Advanced Askama Features
Writing a Custom Filter
// src/filters.rs
use askama::Result;
#[askama::filter_fn]
pub fn to_kebab_case(s: impl std::fmt::Display, _values: &dyn askama::Values) -> Result<String> {
let s = s.to_string();
Ok(s.replace(" ", "-").to_lowercase())
}
// main.rs
mod filters;
use askama::Template;
#[derive(Template)]
#[template(path = "kebab.html")]
struct KebabTemplate {
phrase: String,
}
<!-- templates/kebab.html -->
<p>{{ phrase|to_kebab_case }}</p>
Error Handling
fn main() {
let t = KebabTemplate { phrase: "Hello World".to_string() };
match t.render() {
Ok(html) => println!("{}", html),
Err(e) => eprintln!("Render error: {}", e),
}
}
Integrating with Axum
// Cargo.toml [dependencies]:
// askama = "0.16"
// askama_web = { version = "0.16", features = ["axum-0.8"] }
// axum = "0.8"
// tokio = { version = "1", features = ["full"] }
use askama::Template;
use askama_web::WebTemplate;
use axum::{Router, routing::get};
#[derive(Template, WebTemplate)]
#[template(path = "hello.html")]
struct HelloTemplate {
name: String,
}
async fn hello() -> HelloTemplate {
HelloTemplate { name: "Web".to_string() }
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(hello));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
<!-- templates/hello.html -->
<!DOCTYPE html>
<html>
<head><title>Hello</title></head>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>
Key Points:
WebTemplateimplementsIntoResponsefor the template; it returns HTML with the proper content type and a 500 response on rendering failure. Log failures server-side without leaking details.- The template is rendered and sent as an HTTP response.
- You can add more routes and templates for a multi-page app.
Serving Static Files
You can use tower_http::services::ServeDir (the fs feature) alongside templates, for example with Router::nest_service("/static", ServeDir::new("static")).
Whitespace Control and Configuration
- Use
{%- ... -%}to trim whitespace - Configure Askama with
askama.tomlfor template directories, etc.
Common Misconceptions
- Misconception: “Custom filters can do anything.” Reality: Filters must follow Askama’s signature and error handling rules.
- Misconception: “Templates always panic on error.”
Reality: You can handle errors with
Result.
5. Live Coding Demonstration (20-25 minutes)
Example 1: Custom Filter in Action
// src/filters.rs
#[askama::filter_fn]
pub fn shout(s: impl std::fmt::Display, _values: &dyn askama::Values) -> askama::Result<String> {
let text = s.to_string();
if text.trim().is_empty() {
return Err(askama::Error::custom(std::io::Error::new(
std::io::ErrorKind::InvalidInput, "message must not be empty",
)));
}
Ok(text.to_uppercase() + "!")
}
// main.rs
mod filters;
use askama::Template;
#[derive(Template)]
#[template(path = "shout.html")]
struct ShoutTemplate {
message: String,
}
fn main() {
let t = ShoutTemplate { message: "hello".to_string() };
println!("{}", t.render().unwrap());
}
<!-- templates/shout.html -->
<p>{{ message|shout }}</p>
Example 2: Error Handling
Replace Example 1’s main with this version; the filter rejects empty input at render time:
fn main() {
let t = ShoutTemplate { message: "".to_string() };
match t.render() {
Ok(html) => println!("{}", html),
Err(e) => eprintln!("Error rendering template: {}", e),
}
}
Example 3: Axum Integration
Below is a complete example of integrating Askama with Axum to serve a dynamic web page:
// main.rs
use askama::Template;
use askama_web::WebTemplate;
use axum::{Router, routing::get};
#[derive(Template, WebTemplate)]
#[template(path = "hello.html")]
struct HelloTemplate {
name: String,
}
async fn hello() -> HelloTemplate {
HelloTemplate { name: "Web".to_string() }
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(hello));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
<!-- templates/hello.html -->
<!DOCTYPE html>
<html>
<head><title>Hello</title></head>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>
Key Points:
- The handler returns a
Template+WebTemplatestruct, which implementsIntoResponse. - The template is rendered and sent as an HTTP response.
- You can add more routes and templates for a multi-page app.
Debugging Demonstration
Show common errors and how to fix them:
// Incorrect in Askama 0.16: missing the runtime values argument and Result return type.
fn bad_filter(s: &str) -> String { s.to_owned() }
// Correct:
#[askama::filter_fn]
pub fn good_filter(s: impl std::fmt::Display, _values: &dyn askama::Values) -> askama::Result<String> {
Ok(s.to_string())
}
6. Hands-On Practice (30-40 minutes)
Exercise 1: Basic Practice (10 minutes)
Objective: Write and use a custom filter to reverse a string.
Instructions:
- Create a
filters.rsfile - Write a
reversefilter that reverses the input string - Use it in a template to display reversed text
Starter Code:
#[askama::filter_fn]
pub fn reverse(s: impl std::fmt::Display, _values: &dyn askama::Values) -> askama::Result<String> {
// Fill in here
}
Solution:
#[askama::filter_fn]
pub fn reverse(s: impl std::fmt::Display, _values: &dyn askama::Values) -> askama::Result<String> {
Ok(s.to_string().chars().rev().collect())
}
Exercise 2: Intermediate Challenge (15 minutes)
Objective: Handle a runtime filter error when rendering a template. Missing fields are compile-time errors, not runtime errors.
Requirements:
- Write a custom filter that returns
Err(askama::Error::Custom(...))for empty input - Render with empty data and handle that error
- Print a user-friendly error message
Hints:
- Use
Resultandmatchinmain()
Exercise 3: Creative Application (15 minutes)
Objective: Integrate Askama with Axum to serve a dynamic web page.
Challenge:
- Set up a minimal Axum app
- Create a template that takes a list of items
- Render the template in a handler and serve it at
/items
Extension Ideas:
- Add a custom filter for formatting items
- Serve static CSS for styling
7. Common Pitfalls and Troubleshooting (10 minutes)
Frequent Mistakes
- Problem: Wrong filter signature
Cause: Missing
#[askama::filter_fn], missing&dyn askama::Values, or not returningaskama::Result<T>Solution: Follow Askama 0.16’s filter requirements;impl Displayis a useful input type - Problem: Unhandled render errors
Cause: Using
unwrap()everywhere Solution: Usematchor?to handle errors
Debugging Tips
- Read error messages for filter signature issues
- Test filters with different data
- Use logging for error handling in web apps
8. Best Practices and Professional Tips (5 minutes)
Industry Standards
- Keep filters small and focused
- Handle errors gracefully in production
- Organize filters in a separate module
- Use configuration files for template settings
Next Steps in Your Learning Journey
- Explore advanced Axum features (routing, middleware)
- Write more complex filters and macros
- Read the Askama and Axum documentation for deeper integration
9. Summary and Review (5 minutes)
Key Takeaways
- Custom filters extend Askama’s power
- Error handling prevents crashes and improves UX
- Web integration makes Askama practical for real apps
Quick Review Questions
- What are the required arguments and return type for an Askama 0.16 custom filter?
- How do you handle errors from
render()? - How do you return a template as a response in Axum?
For Instructors
Teaching Tips
- Show filter signature errors and how to fix them
- Demonstrate error handling in both CLI and web contexts
- Use live coding to connect Askama and Axum
Assessment Ideas
- Ask students to write and use a custom filter
- Review error handling in student code
- Peer review: test each other’s web handlers
Adaptation Notes
- For beginners: provide filter and Axum scaffolding
- For advanced students: challenge with more complex filters and error scenarios
- Online: use screen sharing for web integration demos