home ~ projects ~ socials

Run An Axum Web Server With Live Reload From Tauri

This is how I'm embedding Axum server with hot reload in a Tauri app:

[package]
name = "tauri_with_axum"
version = "0.0.1"
description = "Tauri With Axum Embedded"
license = ""
repository = ""
edition = "2021"

[build-dependencies]
tauri-build = { version = "1.5", features = [] }

[dependencies]
tauri = { version = "1.5", features = ["shell-open"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
axum = "0.7.4"
tower-http = { version = "0.5.1", features = ["fs"] }
tower-livereload = "0.9.1"
tokio = { version = "1.35.1"}
notify = "6.1.1"
notify-debouncer-mini = "0.4.1"

[features]
# this feature is used for production builds or when ``devPath`` points to the filesystem
# DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use axum::Router;
use notify::Watcher;
use std::path::Path;
use tower_http::services::ServeDir;
use tower_livereload::LiveReloadLayer;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    tauri::Builder::default()
        .setup(|_app| {
            tauri::async_runtime::spawn(run_web_server());
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");

    Ok(())
}

async fn run_web_server() {
    println!("- Starting web server");
    let livereload = LiveReloadLayer::new();
    let reloader = livereload.reloader();
    let app = Router::new()
        .nest_service(
            "/",
            ServeDir::new(Path::new("/Users/alan/workshop/neopoligen/_site")),
        )
        .layer(livereload);
    if let Ok(mut watcher) = notify::recommended_watcher(move |_| reloader.reload()) {
        if let Ok(_) = watcher.watch(
            Path::new("/Users/alan/workshop/neopoligen/_site"),
            notify::RecursiveMode::Recursive,
        ) {};

        if let Ok(listener) = tokio::net::TcpListener::bind("localhost:3131").await {
            if let Ok(_) = axum::serve(listener, app).await {
                ()
            } else {
                ()
            }
        } else {
            ()
        }
    }
}

Notes

  • The examples for axum and tower/tower_http have a lot of Result<(), Box<dyn std::error::Error>> in them with ? at the end of the await statments. I couldn't figure out how to get that to work so I fell back to if let
  • There's probaby other refinements that could be made, but this is working for me.
-- end of line --

References