home ~ projects ~ socials

Serve Files Embedded In A Rust Binary With axum

Introduction

This is how I'm embedding files in a Rust binary and servering them with axum. Doing so means it's not necessary to have them on the file system when the server is run.

Single File

---
[dependencies]
axum = "0.8.0"
tokio = { version = "1.44.2", features = ["macros", "rt-multi-thread"] }
---

use axum::{Router, response::Html, routing::get};

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(serve_home_page));
    let listener = tokio::net::TcpListener::bind("0.0.0.0:4545").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn serve_home_page() -> Html<&'static str> {
    let response = include_str!("files/index.html");
    Html(response)
}

A Directory

(Note that you can't do this for the root of the site)

---
[dependencies]
axum = "0.8.0"
axum-embed = "0.1.0"
rust-embed = "8.7.0"
tokio = { version = "1.44.2", features = ["macros", "rt-multi-thread"] }
---

use axum::{Router, response::Html, routing::get};
use axum_embed::{FallbackBehavior, ServeEmbed};
use rust_embed::RustEmbed;

#[derive(RustEmbed, Clone)]
#[folder = "src/assets"]
struct Assets;

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(serve_home_page)).nest_service(
        "/assets",
        ServeEmbed::<Assets>::with_parameters(None, FallbackBehavior::NotFound, None),
    );
    let listener = tokio::net::TcpListener::bind("0.0.0.0:4545").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn serve_home_page() -> Html<&'static str> {
    let response = r#"<!DOCTYPE html>
<html>
    <body>
        <h1>Hello, World!</h1>
    </body>
</html>"#;
    Html(response)
}

Details

  • The home page is served via it's own route so it can be set independently.
  • URLs that start with /assets are served from the src/assets directory from the source files.
  • During development the src/assets files are served from the file system. When the binary is built, they get embedded
  • TBD if this works with tower's live reload
-- end of line --