Autumn ships a first-party autumn-admin-plugin that gives you a server-rendered CRUD back-office at /admin (configurable). The autumn generate admin command turns an existing #[model] into a fully-wired AdminModel without hand-writing the adapter.

From autumn new to a working admin page

Shell
# 1. Create the project
autumn new my-app
cd my-app

# 2. Generate the model + migration + repository
autumn generate scaffold Post title:String body:Text published:bool

# 3. Generate the admin adapter for the same model
autumn generate admin Post title:String body:Text published:bool

# 4. Register the plugin and the generated adapter in src/main.rs

After step 3 you have:

Code
src/admin/post.rs        # AdminModel implementation for Post
src/admin/mod.rs         # pub mod post;
tests/post_admin.rs      # smoke tests (anonymous reject + admin list + create)

Wire up the plugin in src/main.rs

Add autumn-admin-plugin to Cargo.toml:

TOML
[dependencies]
autumn-admin-plugin = { workspace = true }

Then register the plugin and your new adapter:

Rust
mod admin;

use autumn_admin_plugin::AdminPlugin;
use admin::post::PostAdmin;

#[autumn_web::main]
async fn main() {
    autumn_web::app()
        .plugin(
            AdminPlugin::new()
                .require_role("admin")
                .register(PostAdmin),
        )
        .routes(routes![...])
        .run()
        .await;
}

Visit http://localhost:3000/admin/posts after running autumn migrate && autumn dev.

What the generator produces

autumn generate admin Post title:String body:Text published:bool creates src/admin/post.rs with a PostAdmin struct that implements every AdminModel method:

OperationAdmin UI action
listPaginated table with search, sort, and filters
getDetail / edit form pre-populated with the current values
createNew record form
updateSave changes to an existing record
deleteSingle-record delete
execute_actionBulk-delete (default) — override for custom bulk actions

Field types are mapped to admin widgets automatically:

Field typeAdmin widgetDefault extras
StringText inputSearchable
TextTextareaSearchable, hidden from list
i32 / i64Number input
boolCheckboxFilterable
f32 / f64Number input
UuidText inputRead-only
NaiveDateTime / DateTimeDate-time pickerRead-only, optional
ByteaHiddenExcluded from update

The id field is always hidden, read-only, and excluded from the list table.

Customising generated field metadata

Pass flags after the field tokens to override individual field behaviour without editing generated internals:

Shell
autumn generate admin User \
  email:String \
  password_hash:String \
  role:String \
  created_at:DateTime \
  --password password_hash \
  --exclude password_hash \
  --select role=admin,editor,viewer \
  --readonly created_at
FlagEffect
--hidden FIELDRender as AdminFieldKind::Hidden (shown in detail, not editable)
--readonly FIELDAdd .readonly() — shown in all views, blocked from create/edit forms
--password FIELDRender as AdminFieldKind::Password (write-only, never displayed)
--select FIELD=val1,val2,...Render as a dropdown; labels are title-cased from values
--exclude FIELDOmit the field from the generated adapter entirely

Multiple flags of the same kind are repeatable:

Shell
autumn generate admin Post title:String slug:String body:Text published:bool \
  --readonly slug \
  --readonly published \
  --exclude body

Search, filter, and sort

The generator derives sensible defaults:

  • Text (String) — included in ILIKE full-text search.
  • Textarea (Text) — searchable but hidden from the list table.
  • Boolean — filterable with true/false/yes/no/1/0.
  • Every field gets a sort arm in apply_sort; the default sort is by id.

To add custom search logic (compound queries, JSONB containment, etc.) edit apply_filters in the generated file — it is ordinary user code.

Security

The admin plugin enforces its own auth gate on every route under /admin:

Rust
AdminPlugin::new().require_role("admin")   // session must have role = "admin"

No route in src/admin/ bypasses this check — the generated adapter only handles the database work, not authentication. Anonymous requests are redirected to the login page (or return 401) before any adapter method runs.

CSRF tokens are injected by the plugin into every state-changing form (POST /admin/{slug}, POST /admin/{slug}/{id}, DELETE /admin/{slug}/{id}) when the app enables CSRF protection. No changes to generated code are needed.

Running the generated smoke tests

Three tests are generated in tests/<snake>_admin.rs:

TestWhat it checks
anonymous_access_is_rejectedGET /admin/{plural} without a session returns 302/401/403
list_loads_for_admin_userWith a valid admin session the list page returns 200
create_redirects_for_admin_userA POST with form data and an admin session returns 200/302/303

Run them against a live server:

Shell
AUTUMN_TEST_BASE_URL=http://localhost:3000 \
AUTUMN_TEST_ADMIN_SESSION=<your_session_cookie> \
cargo test post_admin

If your app enables CSRF in all profiles, configure autumn.toml to skip it in the test profile or supply a valid token in the form body.

--dry-run and --force

Shell
# Preview what would be created — nothing is written
autumn generate admin Post title:String --dry-run

# Overwrite existing src/admin/post.rs after customising the scaffold
autumn generate admin Post title:String --force

--dry-run lists every file and registration change then exits. --force overwrites existing Create targets; idempotent Modify actions (like src/admin/mod.rs) are always safe to re-run.

Updating an existing adapter

The generator creates, not updates. If you add a column to the model and want to reflect it in the admin adapter, re-run with --force:

Shell
autumn generate admin Post title:String body:Text published:bool featured:bool --force

Or edit src/admin/post.rs directly — it is ordinary user code.

Example: blog admin

The examples/blog app uses autumn-admin-plugin with a hand-written PostAdmin in src/admin.rs. Running the generator would produce an equivalent file:

Shell
cd examples/blog
autumn generate admin Post \
  title:String \
  slug:String \
  body:Text \
  published:bool \
  created_at:DateTime \
  updated_at:DateTime \
  --readonly slug \
  --readonly created_at \
  --readonly updated_at

Compare the generated output with examples/blog/src/admin.rs to see the patterns the generator encodes automatically.