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 l'état du projet 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 dans admin! et le CRUD est déjà là — liste, recherche, filtres, permissions, tout y est :
admin! {
article: article::Model => ArticleForm {
title: "Articles",
list_display: [["title", "Title"], ["status", "Status"], ["views", "Views"]],
list_filter: [["status", "Status", 5]],
}
}
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 | admin! panel admin généré |
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 - 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, 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
runique startrégénère le code CRUD admin depuis vos déclarationsadmin!, puis lancecargo runlui-même — une étape de génération one-shot enchaînée au lancement, pas un watcher en tâche de fond (voir Admin (bêta)). Un simplecargo runsaute la régénération.
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 d'un Router Axum classique :
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])
}
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é) : 3.0.3.
CLI
runique fournit :
runique new <name>runique start [--main src/main.rs] [--admin src/admin.rs]— régénère le code admin puis lance l'app (one-shot, pas un watcher)runique create-superuserrunique makemigrations --entities src/entities --migrations migration/src [--force false]runique migration up --migrations migration/src(retour arrière et état :sea-orm-cli migrate down/status)
Admin (bêta)
runique start fait trois choses, dans l'ordre, sur un seul thread :
- il parse vos déclarations
admin!danssrc/admin.rs - il génère le code CRUD sous
src/admins/ - il lance
cargo run --release, bloquant
Il vérifie d'abord si .with_admin(...) existe dans src/main.rs, et ne génère/lance que si c'est le cas — sinon il quitte avec un message expliquant pourquoi. Pas de surveillance continue : relancez runique start pour régénérer après une modification de src/admin.rs.
C'est encore de la bêta : les permissions fonctionnent surtout au niveau des ressources pour l'instant, le dossier généré src/admins/ est écrasé à chaque régénération, et le durcissement est en cours plutôt que terminé.
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 en base.
Référence complète : Sessions
Tests et couverture
- Tests rapportés : 2875 réussis (110 ignorés)
- Snapshot de couverture (
2026-10-08, packagerunique, module admin inclus) : fonctions 88.72%, lignes 88.81%, régions 87.25%
cargo llvm-cov --package runique --features all-databases --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 — tenu à jour au fil de l'avancement
- Changelog
- Runique vs Django — comparatif des fonctionnalités
- Crates.io
- Politique de sécurité
Licence
MIT — voir LICENSE