This project showcases a web server using Axum for routing, Minijinja for templating, and Storyblok for content management—all wrapped up in a set of macros that operate by manipulating data.
Core Ingredients
Axum for Routing
Axum’s async model, powered by Tokio, efficiently routes requests. Whether you’re dealing with static files or dynamic paths, Axum handles concurrency neatly so you can serve multiple requests without skipping a beat.Minijinja for Templating
Minijinja interprets templates in a syntax akin to Jinja from Python. Through a configured environment wrapper (EnvWrapper
), templates likedefault.jinja
orindex.jinja
can be rendered smoothly, keeping your views clean and compartmentalized.Storyblok Integration
You can fetch content from Storyblok by injecting environment variables (ST_TOKEN
andST_BASE_URL
) via your .env file. The macros frommacros.rs
use these values to request data from your Storyblok space.
Macro Magic: get_data!
and extract_components!
get_data!
A convenience macro for fetching data from Storyblok. It sends requests to the base URL with an authorization token, returning a JSON payload that you can pass around your handlers.extract_components!
This macro inspects the JSON payload looking for Storyblok and get the components of yout favorite CMS. Please check out how it works directly from the README of the project: extract_components!: How It's work?.
Why Rust?
Rust's unique selling point is its ability to ensure memory safety without relying on a garbage collector. Through its innovative ownership system and strict compile-time checks, Rust empowers developers to write concurrent code with confidence, avoiding many common pitfalls:
async fn handle_request(req: Request) -> Response {
// Your safe, concurrent code here
}
Key Advantages:
Memory Safety Without Overhead: Rust's ownership model prevents data races and memory leaks at compile-time, without runtime costs.
Zero-Cost Abstractions: Rust allows you to write high-level code that often compiles down to highly efficient machine instructions, rivaling hand-optimized C or C++.
Fearless Concurrency: With Rust's guarantees, you can leverage powerful concurrency patterns without fear of subtle bugs.
Efficient Async I/O: When combined with the Tokio runtime, Axum can efficiently handle numerous concurrent connections:
let app = Router::new()
.route("/", get(handler))
.layer(TraceLayer::new_for_http());
axum::Server::bind(&"0.0.0.0:8000".parse().unwrap())
.serve(app.into_make_service())
.await.unwrap();
Example Benchmark
Rust/Axum | ■■■■■■■■■■■■■■■■■■■■■■ 35k RPS
Node.js | ■■■■■■■■■■■■■ 20k RPS
Python/Flask | ■■■■ 10k RPS
(Fictional numbers, but they illustrate Rust’s high-performance advantage.)
Architecture Overview
+--------------------+ +-----------------------------+
| Axum Router | -------> | Page & Static File Routes |
\+-------------------+ +-----------------------------+
|
v
\+-------------------+ +-----------------------------+
| Minijinja | -------> | Templates (Jinja-style) |
\+-------------------+ +-----------------------------+
|
v
\+-----------------------------+
| Data Macros |
| - get_data! |
| - extract_components! |
\+-----------------------------+
Axum Router: Manages requests, assigning each route to the proper service handler.
Minijinja Environment: Stores compiled templates and renders them on-demand.
Data Macros: Orchestrate the retrieval and transformation of Storyblok content.
Conclusion
This project demonstrates Rust’s power in delivering a web application that’s both performance-focused and flexible enough to work with a headless CMS like Storyblok. Axum provides the concurrency, Minijinja handles the templating, and the macros do the heavy lifting for data retrieval and parsing. Occasional jokes aside, it’s a seriously cool stack. Dive in, and enjoy the speed and safety that Rust brings to modern web development.