Getting Started
Configuration
Basic Connection
#![allow(unused)] fn main() { // Simple connection Database::init("postgres://localhost/mydb").await?; // With TideConfig (recommended) TideConfig::init() .database("postgres://localhost/mydb") .connect() .await?; }
Pool Configuration
#![allow(unused)] fn main() { TideConfig::init() .database("postgres://localhost/mydb") .max_connections(20) // Maximum pool size .min_connections(5) // Minimum idle connections .connect_timeout(Duration::from_secs(10)) .idle_timeout(Duration::from_secs(300)) .max_lifetime(Duration::from_secs(3600)) .connect() .await?; }
Database Types
#![allow(unused)] fn main() { TideConfig::init() .database_type(DatabaseType::Postgres) // or MySQL, SQLite .database("postgres://localhost/mydb") .connect() .await?; }
Resetting Global State
TideORM keeps its active database handle, global configuration, and tokenization settings in process-global state so models can run without threading a connection through every call. That state is now intentionally resettable, which is useful for tests, benchmarks, and applications that need to reconfigure TideORM during the same process lifetime.
#![allow(unused)] fn main() { use tideorm::prelude::*; Database::reset_global(); TideConfig::reset(); TokenConfig::reset(); TideConfig::init() .database("sqlite::memory:") .database_type(DatabaseType::SQLite) .connect() .await?; }
TideConfig::apply() and TideConfig::connect() overwrite previously applied TideORM configuration. If a new configuration omits the database type, the stored type is cleared instead of leaving the old backend classification behind.
Data Type Mappings
Rust to SQL Type Reference
| Rust Type | PostgreSQL | MySQL | SQLite | Notes |
|---|---|---|---|---|
i8, i16 | SMALLINT | SMALLINT | INTEGER | |
i32 | INTEGER | INT | INTEGER | |
i64 | BIGINT | BIGINT | INTEGER | Recommended for primary keys |
u8, u16 | SMALLINT | SMALLINT UNSIGNED | INTEGER | |
u32 | INTEGER | INT UNSIGNED | INTEGER | |
u64 | BIGINT | BIGINT UNSIGNED | INTEGER | |
f32 | REAL | FLOAT | REAL | |
f64 | DOUBLE PRECISION | DOUBLE | REAL | |
bool | BOOLEAN | TINYINT(1) | INTEGER | |
String | TEXT | TEXT | TEXT | |
Option<T> | (nullable) | (nullable) | (nullable) | Wraps any type to make it nullable |
uuid::Uuid | UUID | CHAR(36) | TEXT | |
rust_decimal::Decimal | DECIMAL | DECIMAL(65,30) | TEXT | |
serde_json::Value | JSONB | JSON | TEXT | |
Vec<u8> | BYTEA | BLOB | BLOB | Binary data |
chrono::NaiveDate | DATE | DATE | TEXT | Date only |
chrono::NaiveTime | TIME | TIME | TEXT | Time only |
chrono::NaiveDateTime | TIMESTAMP | DATETIME | TEXT | No timezone |
chrono::DateTime<Utc> | TIMESTAMPTZ | TIMESTAMP | TEXT | With timezone |
Date and Time Types
TideORM provides proper support for all common date/time scenarios:
DateTime with Timezone (Recommended for most cases)
Use chrono::DateTime<Utc> for timestamps that should include timezone information:
#![allow(unused)] fn main() { use chrono::{DateTime, Utc}; #[tideorm::model(table = "sessions")] pub struct Session { #[tideorm(primary_key, auto_increment)] pub id: i64, pub user_id: i64, pub token: String, pub expires_at: DateTime<Utc>, // Maps to TIMESTAMPTZ in PostgreSQL pub created_at: DateTime<Utc>, // Maps to TIMESTAMPTZ in PostgreSQL pub updated_at: DateTime<Utc>, // Maps to TIMESTAMPTZ in PostgreSQL } }
In migrations, use timestamptz():
#![allow(unused)] fn main() { schema.create_table("sessions", |t| { t.id(); t.big_integer("user_id").not_null(); t.string("token").not_null(); t.timestamptz("expires_at").not_null(); t.timestamps(); // Uses TIMESTAMPTZ by default }).await?; }
DateTime without Timezone
Use chrono::NaiveDateTime when you don't need timezone information:
#![allow(unused)] fn main() { use chrono::NaiveDateTime; #[tideorm::model(table = "logs")] pub struct Log { #[tideorm(primary_key, auto_increment)] pub id: i64, pub message: String, pub logged_at: NaiveDateTime, // Maps to TIMESTAMP (no timezone) } }
In migrations, use timestamp():
#![allow(unused)] fn main() { schema.create_table("logs", |t| { t.id(); t.text("message").not_null(); t.timestamp("logged_at").default_now(); }).await?; }
Date Only
Use chrono::NaiveDate for date-only fields:
#![allow(unused)] fn main() { use chrono::NaiveDate; #[tideorm::model(table = "events")] pub struct Event { #[tideorm(primary_key, auto_increment)] pub id: i64, pub name: String, pub event_date: NaiveDate, // Maps to DATE } }
In migrations, use date():
#![allow(unused)] fn main() { schema.create_table("events", |t| { t.id(); t.string("name").not_null(); t.date("event_date").not_null(); }).await?; }
Time Only
Use chrono::NaiveTime for time-only fields:
#![allow(unused)] fn main() { use chrono::NaiveTime; #[tideorm::model(table = "schedules")] pub struct Schedule { #[tideorm(primary_key, auto_increment)] pub id: i64, pub name: String, pub start_time: NaiveTime, // Maps to TIME pub end_time: NaiveTime, } }
In migrations, use time():
#![allow(unused)] fn main() { schema.create_table("schedules", |t| { t.id(); t.string("name").not_null(); t.time("start_time").not_null(); t.time("end_time").not_null(); }).await?; }
Examples
See the TideORM Examples repository for complete working examples.
Testing
Use the smallest command that covers your change.
# Fast library validation
cargo test --lib
# Broad compatibility pass
cargo test --all-features
# Default backend suite
cargo test --features postgres
# SQLite integration suite
cargo test --test sqlite_integration_tests --features "sqlite runtime-tokio" --no-default-features
# PostgreSQL integration suite
cargo test --test postgres_integration_tests
# MySQL integration suite
cargo test --test mysql_integration_tests --features mysql
# SQLite smoke test
cargo test --test sqlite_ci_smoke_test --features "sqlite runtime-tokio" --no-default-features
# Rebuild the book after documentation changes
mdbook build
Integration suites may require local database servers and environment variables. When a test needs a fresh TideORM setup, clear global state first with Database::reset_global(), TideConfig::reset(), and TokenConfig::reset().