Runique — l'expérience développeur Django, en Rust type-safe
Déclarez un modèle une fois, et vous récupérez la table en base, la migration, un formulaire type-safe et un panel admin complet — sans câblage supplémentaire. Runique apporte la productivité de Django à Rust sans vous demander de renoncer à sa sécurité ni à ses performances. Construit sur Axum, SeaORM et Tera, il s'efface une fois le code répétitif écarté.
Statut, sans détour : développement actif. Le crate du framework (
runique) fait foi ;demo-appest une vraie application testée contre lui, pas une démo jetable. Le panel admin est en bêta. Rien ci-dessous n'est enjolivé — voir les limites connues pour la version sans filtre.
🌍 Langues : English | Français
Des macros déclaratives, pas du code répétitif
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! génère l'entité SeaORM (article::Model) et sa migration SQL (runique makemigrations) à partir de la même déclaration. Ajoutez #[form] et vous obtenez un formulaire type-safe correspondant, validé côté serveur et dérivable directement du schéma. Enregistrez la ressource avec le builder typé ModelAdmin et le CRUD est déjà là — liste, recherche, filtres, permissions, tout y est :
use runique::admin::ModelAdmin;
use crate::entities::article;
fn articles() -> ModelAdmin<article::Entity> {
use article::Column::*;
ModelAdmin::new()
.title("Articles")
.list(|l| {
l.display([Title, Status, Views])
.links([Title])
.filter([Status])
})
.form(|f| f.fields([Title, Body, Status]))
}

Pourquoi Runique
Rust a déjà de bonnes briques web, rapides et bas niveau — ce qui lui manque, c'est un framework offrant d'entrée la productivité quotidienne de Django. Assembler soi-même un ORM, un moteur de templates, une couche de formulaires et un admin est un projet à part entière avant même d'avoir écrit une seule fonctionnalité. Runique fait ce câblage à votre place, selon un jeu de conventions cohérent, pour que le temps aille dans votre application plutôt que dans sa plomberie — sans rien perdre de la sécurité des types ni des performances de Rust.
| Django (Python) | Runique (Rust) |
|---|---|
models.py | model! → entité SeaORM + migration |
forms.py | #[form] formulaires type-safe |
admin.py | builder typé ModelAdmin |
urls.py | urlpatterns! macro de routing |
| Templates Django | Tera (auto-échappé) |
| QuerySet | SeaORM + DSL de requêtes search! |
| middleware | slots de middleware ordonnés |
Pour le tableau complet : Runique vs Django.
Sécurité par défaut
Rien de tout ça n'est ajouté après coup — c'est la base dont vous partez :
- le CSRF compare les tokens en temps constant (
ct_eq), pour qu'aucun timing ne trahisse une correspondance, et un formulaire envoyé depuis un autre site est refusé avant le handler - le CSP embarque un nonce par réponse, configurable via le builder
- le login est timing-safe (pas d'énumération d'utilisateurs par le temps de réponse), et les mots de passe sont hashés en Argon2
- les sessions sont persistées en base, leur contenu chiffré, avec priorité donnée aux sessions authentifiées quand la mémoire se tend
- les tokens de réinitialisation de mot de passe vivent en base, hashés SHA-256, à usage unique, durcis contre l'IDOR
- les sorties sont assainies (ammonia) en plus de l'auto-échappement de Tera, et la validation des hôtes autorisés est appliquée
Démarrage rapide
runique new myapp
cd myapp
cargo run # votre app est un binaire Rust normal
Préférez un simple
cargo run. Câblez l'admin avec.with_admin(...)et le builder typéModelAdmindanssrc/admin.rs— pas d'étape de génération (voir Admin (bêta)).
Un main.rs allégé (version complète dans 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(())
}
Les routes passent par la macro urlpatterns! et ressortent sous forme de Routes, que le builder monte sur Axum :
pub fn routes() -> Routes {
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])
}
Pour le guide complet : Installation
Contenu de ce dépôt
runique/— le crate du framework lui-même, le produit et la source de véritédemo-app/— une vraie application construite contre le framework, utilisée pour le validerdocs/— documentation en anglais et en français
Version du workspace (source de vérité) : 4.0.0.
CLI
runique fournit (voir runique/src/bin/runique.rs) :
runique new <name>runique create-superuserrunique test [file] [test]runique makemigrations --entities src/entities --migrations migration/src [--force false]runique migration up | down [-n N] | status(lance le cratemigration/du projet parcargo run, sanssea-orm-cli)
Pas de runique start : l'app est un binaire Rust normal (cargo run). L'admin est du Rust plain (ModelAdmin + .with_admin(...)), sans génération de code.
Admin (bêta)
L'admin est un builder typé — pas de macro, pas de fichiers générés sous src/admins/.
- déclarez chaque ressource avec
ModelAdmin::<Entity>::new()danssrc/admin.rs - enregistrez-les sur
AdminSitevia.with_admin(admin::config)dansmain - lancez avec un simple
cargo run
Les colonnes sont des enums SeaORM (article::Column::Title) : une typo échoue à la compilation ; une colonne secrète (un mot de passe) placée dans une liste ou un formulaire est refusée au démarrage. Permissions, filtres de liste, recherche et formulaires se configurent sur le même builder.
Documentation admin : Admin
Features et bases de données
Activée par défaut : orm, qui apporte SeaORM (entités, requêtes, migrations, sessions) mais aucun pilote de base de données : un build avec orm seul compile, et makemigrations fonctionne, mais l'application ne peut se connecter à rien.
Choisir explicitement un seul backend : sqlite, postgres, mysql, mariadb (mutuellement exclusifs — en activer deux à la fois est une erreur de compilation). Chacun active aussi orm : features = ["postgres"] suffit. La feature all-databases reste disponible pour le tooling multi-moteur qui doit parler à tous les backends à la fois.
Sessions
CleaningMemoryStore remplace le MemoryStore par défaut : il ajoute un nettoyage automatique des sessions expirées, un système de double watermark (128 Mo / 256 Mo) pour borner la mémoire, et une priorité pour les sessions authentifiées — elles sont purgées en dernier sous pression, et survivent aux redémarrages grâce à un fallback chiffré en base.
Référence complète : Sessions
Tests et couverture
- Tests rapportés : 3352 réussis (110 ignorés)
- Snapshot de couverture (
2026-11-04, packagerunique, module admin inclus) : fonctions 92.51%, lignes 92.45%, régions 91.51%
cargo llvm-cov --package runique --features all-databases,admin,test-utils --summary-only
Détail complet par fichier : docs/couverture_test.md
Documentation
- Installation
- Architecture
- Configuration
- Routing
- Formulaires
- Modèle/Schéma
- Templates
- ORM
- Middlewares
- Messages flash
- Exemples
- Admin bêta
- Sessions
- Variables d'environnement
État du projet & ressources
- État du projet — avancement du chantier
makemigrations - Changelog
- Passer de la 3.0 à la 4.0
- Runique vs Django — comparatif des fonctionnalités
- Crates.io
- Politique de sécurité
Licence
MIT — voir LICENSE