Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Server-Side Rendering (SSR)

While RustAPI excels at building JSON APIs, it also supports server-side rendering using the rustapi-view crate, which leverages the Tera template engine (inspired by Jinja2).

Dependencies

Add the following to your Cargo.toml:

[dependencies]
rustapi-rs = { version = "0.1.335", features = ["view"] }
serde = { version = "1.0", features = ["derive"] }

Creating Templates

Create a templates directory in your project root.

templates/base.html (The layout):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}My App{% endblock %}</title>
</head>
<body>
    <nav>
        <a href="/">Home</a>
        <a href="/about">About</a>
    </nav>

    <main>
        {% block content %}{% endblock %}
    </main>

    <footer>
        &copy; 2026 RustAPI
    </footer>
</body>
</html>

templates/index.html (The page):

{% extends "base.html" %}

{% block title %}Home - {{ app_name }}{% endblock %}

{% block content %}
    <h1>Welcome, {{ user.name }}!</h1>

    {% if user.is_admin %}
        <p>You have admin privileges.</p>
    {% endif %}

    <h2>Latest Items</h2>
    <ul>
    {% for item in items %}
        <li>{{ item }}</li>
    {% endfor %}
    </ul>
{% endblock %}

Handling Requests

In your main.rs, initialize the Templates engine and inject it into the application state. Handlers can then extract it using State<Templates>.

use rustapi_rs::prelude::*;
use rustapi_view::{View, Templates};
use serde::Serialize;

#[derive(Serialize)]
struct User {
    name: String,
    is_admin: bool,
}

#[derive(Serialize)]
struct HomeContext {
    app_name: String,
    user: User,
    items: Vec<String>,
}

#[rustapi_rs::get("/")]
async fn index(templates: State<Templates>) -> View<HomeContext> {
    let context = HomeContext {
        app_name: "My Awesome App".to_string(),
        user: User {
            name: "Alice".to_string(),
            is_admin: true,
        },
        items: vec!["Apple".to_string(), "Banana".to_string(), "Cherry".to_string()],
    };

    // Render the "index.html" template with the context
    View::render(&templates, "index.html", context).await
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // 1. Initialize Template Engine
    // Loads all .html files from the "templates" directory
    let templates = Templates::new("templates/**/*.html")?;

    // 2. Add to State
    let app = RustApi::new()
        .state(templates)
        .route("/", get(index));

    println!("Listening on http://localhost:3000");
    app.run("0.0.0.0:3000").await.unwrap();

    Ok(())
}

Template Reloading

In Debug mode (cargo run), rustapi-view automatically reloads templates from disk on every request. This means you can edit your .html files and refresh the browser to see changes instantly without recompiling.

In Release mode (cargo run --release), templates are compiled and cached for maximum performance.

Asset Serving

To serve CSS, JS, and images, use serve_static on the RustApi builder.

let app = RustApi::new()
    .state(templates)
    .route("/", get(index))
    .serve_static("/assets", "assets"); // Serves files from ./assets at /assets