Getting started
Project layout
The files cadmus new writes, and what each one is for.
cadmus new blog writes this:
blog/
├── Cargo.toml Rust's project file: name and dependencies
├── .env secrets for this machine: database address, secret key (not in git)
├── .env.example the same without the secrets, to share
├── .gitignore
├── AGENTS.md how to work on the project, for AI coding agents
└── src/
├── main.rs starts the app: settings, migrations, models, your routes
├── settings.rs how the site behaves: every setting in one place
├── models/
│ ├── mod.rs lists the models, with the admin options for each
│ └── post.rs one file per model (cadmus generate model adds them)
├── migrations/
│ ├── mod.rs the list of migrations, kept up to date by makemigrations
│ └── m0001_initial.rs
└── routes.rs your own pages and endpoints, next to the generated ones
#main.rs
mod migrations;
mod models;
mod routes;
mod settings;
use cadmus::{AdminError, App};
#[tokio::main]
async fn main() -> Result<(), AdminError> {
let app = App::from_settings(settings::settings()?)
.await?
.migrations(migrations::all());
models::register(app).routes(routes::router()).run().await
}
run() reads the command line, so the same program is the web server (serve) and every
management command (migrate, createsuperuser, ...).
#models/mod.rs
pub mod post;
// cadmus:modules (generated modules are added above this line)
pub fn register(app: App) -> App {
app
.model::<post::Entity>(cadmus::ModelOptions::new().label("Posts"))
// cadmus:models (generated models are registered above this line)
}
cadmus generate model adds a line above each marker comment, so keep the two comments.
Change the ModelOptions freely: see Models.
#settings.rs
Every setting of the site, in plain Rust: database, login methods, the admin's look, e-mail,
uploads. Secrets are read from .env. See Settings.
#migrations/
One file per migration, written by cargo run -- makemigrations, and mod.rs, which lists
them in order. You can edit a migration until it has been applied somewhere. See
Migrations.
#routes.rs
Your own axum routes, served next to the generated ones:
pub fn router() -> Router {
Router::new().route("/health", get(|| async { "ok" }))
}
See Your own code.
#.env
DATABASE_URL=sqlite://db.sqlite3?mode=rwc
SECRET_KEY=... # signs logins; `cadmus secret` prints a new one
DEBUG=true # lets the login cookie work over plain http:// on your machine
PUBLIC_URL=http://localhost:3000
.env is loaded automatically on your machine and is in .gitignore. On a server, set the
same names as environment variables instead.