Documentation README

Runique — the Django developer experience, in type-safe Rust

Rust Tests passing License Version Crates.io Runique

Declare a model once, and you get the database table, the migration, a type-safe form, and a full admin panel — no extra wiring. Runique brings Django's productivity to Rust without asking you to give up Rust's safety or performance. It's built on Axum, SeaORM and Tera, and it stays out of your way once the boilerplate is gone.

Status, plainly: active development. The framework crate (runique) is the source of truth; demo-app is a real application exercised against it, not a toy. The admin panel is in beta. Nothing below is dressed up — see the project status for the unfiltered version.

🌍 Languages: English | Français


Declarative macros, not boilerplate

model! {
    Article,
    table: "articles",
    pk: id => Pk,
    enums: { Status: [Draft="Draft", Published="Published"], },
    {
        title:  text [required],
        slug:   text [unique],
        body:   richtext [required],
        status: choice [enum(Status), default: "Draft"],
        views:  int [default: 0],
    }
}

model! generates the SeaORM entity (article::Model) and its SQL migration (runique makemigrations) from the same declaration. Pair it with #[form] and you get a matching type-safe form, validated server-side and derivable straight from the schema. Register the resource in admin! and the CRUD panel is already there — list view, search, filters, permissions, all of it:

admin! {
    article: article::Model => ArticleForm {
        title: "Articles",
        list_display: [["title", "Title"], ["status", "Status"], ["views", "Views"]],
        search_fields: ["title", "body"],
        list_filter:   [["status", "Status", 5]],
    }
}

Why Runique

Rust already has fast, low-level building blocks for the web — what it doesn't have is a framework that gives you Django's day-to-day productivity out of the box. Wiring an ORM, a template engine, a forms layer and an admin together yourself is a project of its own before you've written a single feature. Runique does that wiring for you, following one set of conventions, so the time goes into your app instead of your plumbing — and you keep Rust's type safety and performance the whole way through.

Django (Python)Runique (Rust)
models.pymodel! → SeaORM entity + migration
forms.py#[form] type-safe forms
admin.pyadmin! generated admin panel
urls.pyurlpatterns! routing macro
Django templatesTera (auto-escaped)
QuerySetSeaORM + search! query DSL
middlewareordered middleware slots

For the full picture: Runique vs Django.


Security by default

None of this is bolted on afterward — it's part of the base you start from:

Security policy


Quick start

runique new myapp
cd myapp
cargo run            # your app is a normal Rust binary

runique start regenerates the admin CRUD code from your admin! declarations, then launches cargo run itself — it's a one-shot generation step chained into the launch, not a background watcher (see Admin (beta)). Plain cargo run skips regeneration.

A trimmed-down main.rs (the full version lives in demo-app/src/main.rs):

use runique::prelude::*;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = RuniqueConfig::from_env();
    let db = DatabaseConfig::from_env()?.build().connect().await?;

    RuniqueApp::builder(config)
        .routes(url::routes())
        .with_database(db)
        .statics()
        .build()
        .await
        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?
        .run()
        .await?;
    Ok(())
}

Routes go through the urlpatterns! macro and come out as a regular Axum Router:

pub fn routes() -> Router {
    urlpatterns! {
        "/"          => view!{ index },        name = "index",
        "/blog/{id}" => view!{ blog_detail },  name = "blog_detail",
    }
    .rate_limit("/login", "login", view!(login_user), 10, 60, vec![Method::POST])
}

For the full walkthrough: Installation


What's in this repository

Workspace version (source of truth): 2.2.0.


CLI

runique gives you:

⚠️ A note on rolling back migrations runique makemigrations writes migrations that keep the chronological order of the migration system intact. If you ever need to roll one back, reach for the SeaORM CLI instead — it keeps the migration tracking table in sync with the schema's actual state. Mixing the two rollback paths can desynchronize that tracking.


Admin (beta)

runique start does three things, in order, on a single thread:

  1. parses your admin! declarations in src/admin.rs
  2. generates the CRUD code under src/admins/
  3. runs cargo run --release, blocking

It checks for .with_admin(...) in src/main.rs first and only generates/launches if that's present — otherwise it exits with a message telling you why. There's no continuous watching: run runique start again to regenerate after editing src/admin.rs.

It's still beta: permissions work mainly at the resource level for now, the generated src/admins/ folder gets overwritten on each regeneration, and hardening is ongoing rather than finished.

Admin docs: Admin


Features and database backends

Enabled by default: orm, all-databases.

Pick a specific backend instead: sqlite, postgres, mysql, mariadb.


Sessions

CleaningMemoryStore stands in for the default MemoryStore, adding automatic cleanup of expired sessions, a two-tier watermark (128 MB / 256 MB) to keep memory bounded, and priority for authenticated sessions — they're the last to be purged under pressure, and they survive restarts through a database fallback.

Full reference: Sessions


Tests and coverage

cargo llvm-cov --package runique --summary-only

Full per-file breakdown: docs/couverture_test.md


Documentation


Project status & resources


License

MIT — see LICENSE