<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: itsuki</title>
    <description>The latest articles on DEV Community by itsuki (@seballiot).</description>
    <link>https://dev.to/seballiot</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2477061%2F2de41c2a-9349-49ef-bc0f-1db5a7fead31.png</url>
      <title>DEV Community: itsuki</title>
      <link>https://dev.to/seballiot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/seballiot"/>
    <language>en</language>
    <item>
      <title>I Built a Django-Inspired Web Framework in Rust — Here's What I Learned</title>
      <dc:creator>itsuki</dc:creator>
      <pubDate>Sun, 14 Jun 2026 14:18:48 +0000</pubDate>
      <link>https://dev.to/seballiot/i-built-a-django-inspired-web-framework-in-rust-heres-what-i-learned-5bbf</link>
      <guid>https://dev.to/seballiot/i-built-a-django-inspired-web-framework-in-rust-heres-what-i-learned-5bbf</guid>
      <description>&lt;p&gt;If you've ever used Django, you know the feeling: one command, and you have an admin panel, an ORM, form validation, middleware, session handling — everything just works. Then you try Rust web development, and you're back to assembling pieces yourself.&lt;/p&gt;

&lt;p&gt;That's why I built &lt;strong&gt;Runique&lt;/strong&gt;: a batteries-included web framework for Rust, inspired by Django's philosophy, built on top of Axum and Tokio.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why "Django for Rust"?
&lt;/h2&gt;

&lt;p&gt;Existing Rust frameworks are excellent at what they do — Axum is fast and composable, Actix-Web is a performance beast — but they're low-level by design. You bring your own ORM, your own session store, your own CSRF protection, your own admin interface. For many projects, that's the right tradeoff.&lt;/p&gt;

&lt;p&gt;Runique takes the opposite bet: &lt;strong&gt;convention over configuration&lt;/strong&gt;, with a structured, opinionated setup that gets you from zero to a production-ready app fast, without sacrificing Rust's safety and performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Builder: A Validated Construction Pipeline
&lt;/h2&gt;

&lt;p&gt;The part I'm most proud of is the application builder. You declare your components with a fluent API — in any order — and then a single &lt;code&gt;.build()&lt;/code&gt; call runs a &lt;strong&gt;fixed, validated construction pipeline&lt;/strong&gt; at startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Validation → DB connection → Templates → Engine → Admin → Middleware → Static files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here's what a typical app setup looks like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RuniqueAppBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.routes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;url&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.with_database_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.with_mailer_from_env&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.statics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.middleware&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
        &lt;span class="nf"&gt;.with_session_memory_limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.with_session_cleanup_interval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.with_allowed_hosts&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="nf"&gt;.enabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.host&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"example.com"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="nf"&gt;.with_csp&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
            &lt;span class="nf"&gt;.policy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;SecurityPolicy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="nf"&gt;.with_header_security&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.scripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"'self'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"'strict-dynamic'"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="nf"&gt;.with_anti_bot&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.with_admin&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="nf"&gt;.routes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;admins&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/admin"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;.build()&lt;/code&gt; call validates every component — including cross-dependencies — before constructing anything. If your &lt;code&gt;SECRET_KEY&lt;/code&gt; is still the default insecure value in production, the build &lt;strong&gt;fails with a clear error and a fix suggestion&lt;/strong&gt;, before a single request is served:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Security] SECRET_KEY is using the default insecure value
  → Set SECRET_KEY to a random 32+ character string in your .env file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is the Django &lt;code&gt;check&lt;/code&gt; framework equivalent, but enforced at startup — not discovered in production.&lt;/p&gt;


&lt;h2&gt;
  
  
  Security Included by Default
&lt;/h2&gt;

&lt;p&gt;Security in Runique isn't a plugin you add later. It's part of the construction pipeline itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CSP (Content Security Policy)&lt;/strong&gt; — configurable profiles, per-request nonce, HTMX hash merging when the admin panel is enabled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSRF protection&lt;/strong&gt; — built into the middleware stack, with per-route exemptions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Host validation&lt;/strong&gt; — allowed hosts checked before routing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trusted proxies&lt;/strong&gt; — explicit configuration, not implicit trust&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security headers on static files&lt;/strong&gt; — &lt;code&gt;X-Content-Type-Options&lt;/code&gt;, &lt;code&gt;Strict-Transport-Security&lt;/code&gt;, &lt;code&gt;X-Frame-Options&lt;/code&gt;, &lt;code&gt;Referrer-Policy&lt;/code&gt; applied automatically
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// You don't wire ServeDir yourself — `.statics()` does it,&lt;/span&gt;
&lt;span class="c1"&gt;// already wrapping every static/media route with security headers:&lt;/span&gt;
&lt;span class="nf"&gt;.statics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// → X-Content-Type-Options: nosniff&lt;/span&gt;
&lt;span class="c1"&gt;//   Strict-Transport-Security: max-age=31536000; includeSubDomains; preload&lt;/span&gt;
&lt;span class="c1"&gt;//   X-Frame-Options: DENY&lt;/span&gt;
&lt;span class="c1"&gt;//   Referrer-Policy: strict-origin-when-cross-origin&lt;/span&gt;
&lt;span class="c1"&gt;//   + cache-control&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The middleware stack uses &lt;strong&gt;numbered slots&lt;/strong&gt; to guarantee application order — you can't accidentally apply CSRF before sessions, because the slots enforce the correct sequence.&lt;/p&gt;


&lt;h2&gt;
  
  
  What's Included
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ORM&lt;/strong&gt; (via SeaORM, optional feature flag)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Template engine&lt;/strong&gt; (Tera, with custom filters and a URL registry for named routes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin panel&lt;/strong&gt; (auto-generated from your models, merged before the middleware stack so it always has session/auth context)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Form engine&lt;/strong&gt; (typed structs with validation, v2 in progress)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session handling&lt;/strong&gt; (memory-first store with database fallback, built on &lt;code&gt;tower-sessions&lt;/code&gt;, with periodic cleanup)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;i18n&lt;/strong&gt; (the FR/EN bilingual doc site is itself built with Runique)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Middleware system&lt;/strong&gt; with ordered slots&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password reset&lt;/strong&gt; (pluggable, routes registered automatically during build)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debug error page&lt;/strong&gt; — a styled page with collapsible sections and copy-to-clipboard for stack traces&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  One Concrete Example: Admin Merge Order
&lt;/h2&gt;

&lt;p&gt;Here's a subtle design decision that illustrates Runique's philosophy. In Axum, &lt;code&gt;.layer()&lt;/code&gt; only applies to routes present at call time. This means if you merge your admin router after applying middleware, the admin routes run &lt;strong&gt;without&lt;/strong&gt; session, CSRF, or extension context — a silent, hard-to-debug failure.&lt;/p&gt;

&lt;p&gt;Runique handles this in the builder:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Step 4b: admin + password reset — merged BEFORE the middleware stack.&lt;/span&gt;
&lt;span class="c1"&gt;// `.layer()` in Axum only covers routes present at call time;&lt;/span&gt;
&lt;span class="c1"&gt;// merging after means admin routes run without Session/CSRF/Extensions.&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.admin.enabled&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="nf"&gt;.merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin_router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;router&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Step 5: middleware applied AFTER, covering everything including admin&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="nf"&gt;.apply_to_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You don't have to think about this. The pipeline handles it.&lt;/p&gt;


&lt;h2&gt;
  
  
  Current Status
&lt;/h2&gt;

&lt;p&gt;Runique is at &lt;strong&gt;v2.1.x&lt;/strong&gt;, MIT licensed, with bilingual documentation (EN/FR) at &lt;a href="https://runique.io" rel="noopener noreferrer"&gt;runique.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The framework is used in production for a restaurant management system — dogfooding at its most direct.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's missing&lt;/strong&gt; (being honest here, Django-style):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No async task queue yet (background jobs run on bare &lt;code&gt;tokio::spawn&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Email sending is partial&lt;/li&gt;
&lt;li&gt;OAuth is planned but not yet implemented&lt;/li&gt;
&lt;li&gt;Test coverage is at ~61% (growing)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;Add the dependency to your &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;runique&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2.1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://runique.io/docs/en" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Read the Getting Started Guide&lt;/a&gt;
&lt;/p&gt;
&lt;h2&gt;
  
  
  Links &amp;amp; Resources
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/seb-alliot" rel="noopener noreferrer"&gt;
        seb-alliot
      &lt;/a&gt; / &lt;a href="https://github.com/seb-alliot/runique" rel="noopener noreferrer"&gt;
        runique
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A framework web base on Django/python
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Runique — Django-inspired Rust Framework&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e679970fb265bfb7428ba13c738c5b846c7061ae55e21fb646fac5723f3b6389/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f727573742d312e38382532422d6f72616e6765"&gt;&lt;img src="https://camo.githubusercontent.com/e679970fb265bfb7428ba13c738c5b846c7061ae55e21fb646fac5723f3b6389/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f727573742d312e38382532422d6f72616e6765" alt="Rust"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/c0cf2fcd1313f38cbc530d1e1bcc305a744ebc6956b8fbf193470253b74c003b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d3230313125324225323070617373696e672d677265656e"&gt;&lt;img src="https://camo.githubusercontent.com/c0cf2fcd1313f38cbc530d1e1bcc305a744ebc6956b8fbf193470253b74c003b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d3230313125324225323070617373696e672d677265656e" alt="Tests passing"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e"&gt;&lt;img src="https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e" alt="License"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/15c0089bcef55466b3c40d3b1e19b644423c086afeb64aee7de6b5d83db21887/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d322e312e31362d626c7565"&gt;&lt;img src="https://camo.githubusercontent.com/15c0089bcef55466b3c40d3b1e19b644423c086afeb64aee7de6b5d83db21887/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d322e312e31362d626c7565" alt="Version"&gt;&lt;/a&gt;
&lt;a href="https://crates.io/crates/runique" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/2681e8d70b5efe4826f355b9f62a280f65466ed3b0605c911fa5b3082112b5c4/68747470733a2f2f696d672e736869656c64732e696f2f6372617465732f762f72756e69717565" alt="Crates.io"&gt;&lt;/a&gt;
&lt;a href="https://runique.io" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/96fcf63b7ce9ce7b9f5585864aa409f4ab1eea14f531e708f3be479d0ac07b9f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52756e697175652d627269676874677265656e" alt="Runique"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Runique is a web framework built on Axum, focused on type-safe forms, security middleware, template rendering, ORM integration, and a code-generated admin workflow.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Current state: active development. The framework source of truth is the &lt;code&gt;runique&lt;/code&gt; crate
&lt;code&gt;demo-app&lt;/code&gt; is used as a validation/testing application for framework behavior.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;🌍 &lt;strong&gt;Languages&lt;/strong&gt;: English | &lt;a href="https://runique.io/readme/fr" rel="nofollow noopener noreferrer"&gt;Français&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What this repository contains&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;runique/&lt;/code&gt; → framework crate (main product)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;demo-app/&lt;/code&gt; → test/validation app for framework development&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docs/&lt;/code&gt; → EN/FR documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Workspace version (source of truth): &lt;strong&gt;2.1.16&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Core capabilities&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Type-safe form system (&lt;code&gt;forms&lt;/code&gt;, extractors, validators, renderers)&lt;/li&gt;
&lt;li&gt;Routing macros and URL helpers&lt;/li&gt;
&lt;li&gt;Tera template integration and context helpers&lt;/li&gt;
&lt;li&gt;Security middleware (CSRF, CSP, allowed hosts, sanitization, auth/session)&lt;/li&gt;
&lt;li&gt;SeaORM integration + migration tooling&lt;/li&gt;
&lt;li&gt;Flash message system&lt;/li&gt;
&lt;li&gt;Admin beta (&lt;code&gt;admin!&lt;/code&gt; macro + daemon-generated CRUD code)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Main public modules are exposed from &lt;code&gt;runique/src/lib.rs&lt;/code&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/seb-alliot/runique
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; runique
cargo build --workspace
cargo &lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/seb-alliot/runique" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Documentation: &lt;a href="https://runique.io" rel="noopener noreferrer"&gt;runique.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Crates.io: &lt;a href="https://crates.io/crates/runique" rel="noopener noreferrer"&gt;crates.io/crates/runique&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What do you think?
&lt;/h2&gt;

&lt;p&gt;I'm particularly curious to hear from:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Django developers&lt;/strong&gt; — Does this structure feel familiar, or "too much" for the Rust ecosystem?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rust experts&lt;/strong&gt; — How would you handle the middleware ordering differently?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's discuss in the comments!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webdev</category>
      <category>django</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
