بنقرة واحدة
toasty-guidance
// Guidance for using the Toasty async ORM crate — schema definition, CRUD, relations, queries, and transactions
// Guidance for using the Toasty async ORM crate — schema definition, CRUD, relations, queries, and transactions
[Critical] Best practice for Rust coding. Always activate this skill before authoring Rust code or answering questions about Rust.
Find sources for a Rust crate from crates.io. Activate this skill to inspect a crate's API.
Idiomatic patterns for writing code with formality-core (a-mir-formality). Use when writing or reviewing judgment functions, type definitions, or parser-related code in projects built on formality-core.
Always use this skill before writing any test code in the Toasty repository
| name | toasty-guidance |
| description | Guidance for using the Toasty async ORM crate — schema definition, CRUD, relations, queries, and transactions |
| crates | toasty |
| activation | always |
Toasty is an async ORM for Rust supporting SQL (SQLite, PostgreSQL, MySQL) and NoSQL (DynamoDB). It prioritizes type safety and leans into each database's capabilities rather than hiding them.
Define models with #[derive(toasty::Model)]. Mark the primary key with #[key] and auto-generated fields with #[auto] (auto-increment for integers, UUID v7 for uuid::Uuid).
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: i64,
#[unique]
email: String,
name: String,
#[has_many]
todos: toasty::HasMany<Todo>,
}
#[derive(Debug, toasty::Model)]
struct Todo {
#[key]
#[auto]
id: i64,
#[index]
user_id: i64,
#[belongs_to(key = user_id, references = id)]
user: toasty::BelongsTo<User>,
title: String,
}
Use #[derive(toasty::Embed)] for value types that flatten into the parent table:
#[derive(toasty::Embed)]
struct Address {
street: String,
city: String,
}
Register all models with the builder and connect:
let db = toasty::Db::builder()
.register::<User>()
.register::<Todo>()
.connect("sqlite://memory")
.await?;
Connection strings: sqlite://memory, sqlite:///path/to/db, postgresql://user:pass@host/db, mysql://user:pass@host/db, dynamodb://region.
let user = User::create()
.name("Alice")
.email("alice@example.com")
.todo(Todo::create().title("Task 1")) // nested create
.exec(&mut db)
.await?;
// By primary key (generated finder)
let user = User::get_by_id(&mut db, &id).await?;
// All records
let users = User::all().exec(&mut db).await?;
// Filter
let users = User::all()
.and(User::fields().name().eq("Alice"))
.exec(&mut db)
.await?;
// Single result
let user = User::all()
.and(User::fields().email().eq("a@b.com"))
.one()
.exec(&mut db)
.await?;
// Optional result
let user = User::all()
.and(User::fields().email().eq("a@b.com"))
.first()
.exec(&mut db)
.await?;
// Count
let n = User::all().count().exec(&mut db).await?;
user.update()
.name("Bob")
.exec(&mut db)
.await?;
User::all()
.and(User::fields().active().eq(false))
.delete()
.exec(&mut db)
.await?;
Build type-safe filters using generated field paths:
User::fields().age().gt(18)
User::fields().age().ge(21)
User::fields().name().ne("admin")
User::fields().id().is_in([1, 2, 3])
User::fields().phone().is_some()
User::fields().phone().is_none()
// Combine with boolean logic
User::fields().age().gt(18).and(User::fields().active().eq(true))
The query! macro offers a shorthand:
toasty::query!(User filter .name == "Alice" && .age > 18)
Three relation types: HasMany<T>, HasOne<T>, BelongsTo<T>.
Lazy loading (default) -- access a relation to load it on demand:
let todos = user.todos().exec(&mut db).await?;
let owner = todo.user().exec(&mut db).await?;
Eager loading -- preload with .include():
let users = User::all()
.include(User::fields().todos())
.exec(&mut db)
.await?;
Cursor-based pagination:
let page = User::all().paginate(10).exec(&mut db).await?;
for u in &page.items { /* ... */ }
if page.has_next() {
let next = page.next(&mut db).await?;
}
let mut tx = db.transaction().await?;
User::create().name("Alice").exec(&mut tx as &mut dyn toasty::Executor).await?;
tx.commit().await?;
// Drop without commit = automatic rollback
Nested transactions (savepoints) are supported via tx.transaction().await?.
Execute independent statements together:
use toasty::batch;
let (users, posts) = batch((
User::all(),
Post::all(),
)).exec(&mut db).await?;
Executor trait is implemented by both Db and Transaction -- pass &mut db or &mut tx interchangeably.Db owns a connection pool (deadpool) and is cheap to clone.db.connection().await?..get() before loading panics. Use .include() for eager loading or call the relation method to load lazily.T is the returning type, not the model (e.g., Query<List<User>> returns Vec<User>).See the LLM.txt resource within this skill for the full API reference.