Relationships
Relationships
Associations between store resources. Relationships define how resources connect to each other: one-to-many, many-to-one, one-to-one, and many-to-many. They compile to Ash Framework relationships at runtime, enabling you to load related data, filter across associations, and build aggregates.
When to use
Use relationships when you need to:
- Model parent-child connections between resources (e.g., orders and line items)
- Navigate from one resource to its related records
- Filter or sort by properties of related resources
- Compute aggregates across relationships (e.g., count of items per order)
- Enforce referential integrity between resources
Relationship types
| Type | Description | Foreign key location |
|---|---|---|
has_many | One-to-many. The parent has many children. | On the child resource (e.g., order_id on line_item) |
belongs_to | Many-to-one. The child references its parent. | On this resource (e.g., order_id on line_item) |
has_one | One-to-one. The parent has exactly one related record. | On the related resource |
many_to_many | Many-to-many via a join table. | In a separate join table |
Syntax
resource <resource_name> has_many <name>: <target_resource> belongs_to <name>: <target_resource> has_one <name>: <target_resource> many_to_many <name>: <target_resource>Parameters
| Parameter | Required | Description |
|---|---|---|
name | Yes | Bare identifier naming this relationship. Used to reference the related data in queries and expressions. |
target_resource | Yes | Bare identifier of the related resource. Must be a resource defined in the same store. |
Examples
One-to-many with belongs_to
stores store content source: managed
resource author id as uuid, is primary_key name as text, is required timestamps
has_many articles: article
resource article id as uuid, is primary_key title as text, is required author_id as uuid, is required timestamps
belongs_to author: authorThe article resource has an author_id field that references author. From an author, you can traverse has_many articles to get all their articles. From an article, you can traverse belongs_to author to get its author.
One-to-one
stores store accounts source: managed
resource user id as uuid, is primary_key email as text, is required timestamps
has_one profile: profile
resource profile id as uuid, is primary_key user_id as uuid, is required display_name as text bio as text timestamps
belongs_to user: userMany-to-many
stores store library source: managed
resource book id as uuid, is primary_key title as text, is required timestamps
many_to_many tags: tag
resource tag id as uuid, is primary_key name as text, is required
many_to_many books: book
identity unique_name: [name]Many-to-many relationships require a join table. Mashin creates the join table automatically for managed stores.
Relationships with aggregates
resource order id as uuid, is primary_key customer_email as text, is required timestamps
has_many line_items: line_item aggregate item_count: count(line_items) aggregate order_total: sum(line_items, :price)Aggregates compute values across relationships. item_count returns how many line items an order has. order_total sums the price field across all line items.
Governance
Relationships are structural declarations. They do not require governance approval and do not produce ledger events on their own. However, loading related data through a relationship is part of a read action and is governed accordingly. Policies on the related resource apply when traversing relationships.
Translations
| Language | has_many | belongs_to | has_one | many_to_many |
|---|---|---|---|---|
| English | has_many | belongs_to | has_one | many_to_many |
Relationship keywords are not currently translated. They use English in all locales.
See also
- resource - Resource declarations
- identity - Unique constraints
- read - Read actions that can filter across relationships
- field types - Field type reference