A complete example
Let's put the pieces together. We have teams, each with zero or more players, and two tables that already exist:
CREATE TABLE teams (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE players (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
team_id BIGINT NOT NULL REFERENCES teams(id),
name TEXT NOT NULL
);
This is PostgreSQL SQL; another backend needs its own identity and foreign-key syntax. Configure a postgresql datasource as in Configure datasources, then write the models and the read:
use canyon_sql::{ core::Canyon, crud::Read, macros::{canyon_entity, CanyonMapper, Crud, Fields}, CanyonResult, }; #[derive(Debug, Fields, Crud, CanyonMapper)] #[canyon_entity(table_name = "teams")] struct Team { #[primary_key] id: i64, name: String, } #[derive(Debug, Fields, Crud, CanyonMapper)] #[canyon_entity(table_name = "players")] struct Player { #[primary_key] id: i64, #[foreign_key(references = Team::id)] team_id: i64, name: String, } #[tokio::main] async fn main() -> CanyonResult<()> { Canyon::init().await?; let teams = Team::find_all().await?; for team in teams { let players = Player::find_all_by_team(&team).await?; println!("{} has {} players", team.name, players.len()); } Ok(()) }
Follow the loop in main: Canyon reads every team, then reads that team's players. Two declarations make this possible:
#[foreign_key(references = Team::id)]generates the Rust lookup methods.REFERENCES teams(id)makes PostgreSQL enforce the relationship.
A team with no players prints 0. If either query or row mapping fails, ? returns that error from main; the loop does not quietly skip the team.
You can grow this example in several directions:
- Filter teams with
Team::select_query()?andTeamFieldValue. - Add writes with the
Insert,Update, orDeletetraits. - Put writes behind a repository adapter. Read that chapter's table-name limitation first: this example uses
teams, not the defaultteam.
The schema, permissions, transaction boundaries, and meaning of a missing row remain application decisions. Canyon handles the repeated database plumbing around them.