Quick Start
This guide walks you through a complete Rango setup in under 5 minutes.
1. Initialize
cargo new my-app && cd my-app
rango init --backend sqlite
Add Rango to Cargo.toml:
[dependencies]
rango-orm = { version = "0.2", default-features = false, features = ["sqlite"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1"
uuid = { version = "1", features = ["v4"] } # you add these two: Rango does not re-export them
chrono = "0.4"
2. Define a model
Create src/models.rs:
#![allow(unused)] fn main() { use rango::prelude::*; #[derive(Model)] pub struct User { pub id: FieldUuid, #[field(unique)] pub email: FieldEmail, pub name: FieldVarchar<1, 100>, pub active: FieldBool, pub created_at: FieldDateTime, } #[derive(Model)] pub struct Post { pub id: FieldUuid, pub author_id: ForeignKey<User>, pub title: FieldVarchar<1, 255>, pub body: FieldText, pub published: FieldBool, } }
3. Generate migrations
rango makemigrations
Output:
🔍 Scanning models in src... (backend: sqlite)
Found 2 model(s):
- user (5 columns)
- post (5 columns)
✅ Migration generated: migrations/0001_post_user.sql
📸 Snapshot updated.
Migration files are named NNNN_<tables>.sql, with the tables involved in alphabetical order.
4. Apply migrations
DATABASE_URL=sqlite://db.sqlite3 rango migrate
Output:
🔌 Connecting to database (sqlite)...
▶ Applying 0001_post_user.sql...
✅ 0001_post_user.sql
✅ Applied 1 migration(s).
5. Use the ORM
use rango::prelude::*; mod models; use models::User; #[tokio::main] async fn main() -> anyhow::Result<()> { let pool = connect_sqlite("sqlite://db.sqlite3").await?; // Insert let user = User { id: FieldUuid(uuid::Uuid::new_v4()), email: FieldEmail::new("alice@example.com")?, name: FieldVarchar::new("Alice")?, active: FieldBool(true), created_at: FieldDateTime(chrono::Utc::now()), }; let user = insert(&pool, user).await?; // Query let active_users = User::filter(&pool) .eq(User::ACTIVE, true) .order_by(User::NAME) .limit(10) .all() .await?; for u in &active_users { println!("{} — {}", u.name.as_str(), u.email.as_str()); } Ok(()) }
User::ACTIVE and User::NAME are typed columns generated by #[derive(Model)]: a typo or a wrong value type is a compile error. String names (.eq("active", true)) also work; they are checked against the model's schema at run time and an unknown column returns Error::Invalid.
Connecting to PostgreSQL or MySQL
connect_sqlite and connect_mysql take a URL; connect_postgres takes a DatabaseConfig (pool size, timeouts):
#![allow(unused)] fn main() { // PostgreSQL (default features of `rango-orm`) let pool = connect_postgres(&DatabaseConfig::from_url("postgres://user:pass@localhost:5432/mydb")).await?; // MySQL / MariaDB let pool = connect_mysql("mysql://user:pass@localhost:3306/mydb").await?; }