#axum #response #protocols #js #page #json-response #inertia

axum-inertia

An implementation of the Inertia.js protocol for Axum

4 releases (2 breaking)

0.3.0 Feb 13, 2024
0.2.0 Nov 28, 2023
0.1.1 Nov 11, 2023
0.1.0 Nov 1, 2023

#1862 in Encoding

MIT/Apache

26KB
491 lines

Crates.io Documentation

axum-inertia

Implementation of the inertia.js protocol for axum.

Provides an Inertia axum extractor to render responses like so:

async fn get_posts(i: Inertia) -> impl IntoResponse {
    i.render("Posts/Index", json!({ "posts": vec!["post one", "post two"] }))
}

See crate documentation for more information.


lib.rs:

An implementation of the inertia.js protocol for axum.

The basic idea is that any axum handler that accepts the Inertia struct as a function parameter is an inertia endpoint. For instance:

use axum_inertia::Inertia;
use axum::{Json, response::IntoResponse};
use serde_json::json;

async fn my_handler_fn(i: Inertia) -> impl IntoResponse {
    i.render("Pages/MyPageComponent", json!({"myPageProps": "true"}))
}

This does the following:

  • If the incoming request is the initial page load (i.e., does not have the X-Inertia header set to true), the render method responds with an html page, which is configurable when setting up the initial Inertia state (see Getting started below).

  • Otherwise, the handler responses with the standard inertia "Page" object json, with the included component and page props passed to render.

  • If the request has a mismatching asset version (again, this is configurable), the handler responds with a 409 Conflict to tell the client to reload the page. The function body of the handler is not executed in this case.

Getting started

First, you'll need to provide your axum routes with [InertiaConfig] state. This state boils down to two things: an optional string representing the asset version and a function that takes serialized props and returns an HTML string for the initial page load.

The [vite] module provides a convenient way to set up this state with axum::Router::with_state. For instance, the following code sets up a standard development server:

use axum_inertia::{vite, Inertia};
use axum::{Router, routing::get, response::IntoResponse};

// Configuration for Inertia when using `vite dev`:
let inertia = vite::Development::default()
    .port(5173)
    .main("src/main.ts")
    .lang("en")
    .title("My inertia app")
    .into_config();
let app: Router = Router::new()
    .route("/", get(get_root))
    .with_state(inertia);

The [Inertia] struct is then available as an axum Extractor and can be used in handlers like so:

use axum::response::IntoResponse;
use serde_json::json;

async fn get_root(i: Inertia) -> impl IntoResponse {
    i.render("Pages/Home", json!({ "posts": vec!["post one", "post two"] }))
}

The Inertia::render method takes care of building a response conforming to the inertia.js protocol. It takes two parameters: the name of the component to render, and the page props (serializable to json).

Using the extractor in a handler requires that you use axum::Router::with_state to initialize Inertia in your routes. In fact, it won't compile if you don't!

Using InertiaConfig as substate

It's likely you'll want other pieces of state beyond [InertiaConfig]. You'll just need to implement axum::extract::FromRef for your state type for [InertiaConfig]. For instance:

use axum_inertia::{vite, Inertia, InertiaConfig};
use axum::{Router, routing::get, extract::FromRef};

#[derive(Clone)]
struct AppState {
    inertia: InertiaConfig,
    name: String
}

impl FromRef<AppState> for InertiaConfig {
    fn from_ref(app_state: &AppState) -> InertiaConfig {
        app_state.inertia.clone()
    }
}

let inertia = vite::Development::default()
    .port(5173)
    .main("src/main.ts")
    .lang("en")
    .title("My inertia app")
    .into_config();
let app_state = AppState { inertia, name: "foo".to_string() };
let app: Router = Router::new()
    .route("/", get(get_root))
    .with_state(app_state);

Configuring development and production

See the [vite] module for more information.

Dependencies

~6.5–9MB
~162K SLoC