# Define Relation

> Use when defining ROM Relations that map to database tables in Hanami 2.x — inherit from `Hanami::DB::Relation` with `schema :table_name, infer: true` for automatic schema introspection or explicit `schema :table_name do ... end` with typed attributes, add custom query methods as public relation methods returning filtered/reordered relations, define associations via `many_to_one`/`one_to_many` with `as:` aliases and load strategies using `combine` for eager-loading, and keep relations in sync with migrations by verifying via console. Trigger terms: ROM Relations, schema inference, associations, query methods, combine, eager-loading.

- Skill: `igmarin/define-relation` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add igmarin/define-relation`
- Raw SKILL.md: https://api.skillmd.com/api/skills/igmarin/define-relation/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- License: MIT
- Author: igmarin (https://skillmd.com/u/igmarin)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/igmarin/define-relation

---


# define-relation

Use this skill when defining ROM Relations that map to database tables in Hanami 2.x.

**Core principle:** The Relation is the query layer. It defines how to read and filter data, not business logic.

---

## Quick Reference

| Scenario | Approach |
|---|---|
| Define a Relation | Class inherits from `Hanami::DB::Relation` with `schema :table_name, infer: true` |
| Infer schema from DB | `schema :table_name, infer: true` |
| Define explicit schema | `schema :table_name do { attribute :id, Types::Integer } end` |
| Add a custom query method | Define a method that returns a restricted/ordered relation |
| Define an association | `many_to_one :users, as: :user` or `one_to_many :posts, as: :posts` |
| Access the Relation in an Action | `include Deps["relations.users"]` |
| Call a query method | `relations.users.active` or `relations.users.by_email("a@b.com")` |
| Verify inferred schema | `bundle exec hanami console` → `app['relations.users'].schema` |

---

## Core Rules

1. **Create the Relation file** in the app or slice:

   ```ruby
   # app/relations/users.rb
   # frozen_string_literal: true

   module MyApp
     module Relations
       class Users < Hanami::DB::Relation
         schema :users, infer: true
       end
     end
   end
   ```

2. **Use `infer: true`** for new tables where the database schema is the source of truth. ROM will introspect the table at boot.

3. **Define explicit schema** when you need custom type coercion or virtual attributes:

   ```ruby
   schema :users do
     attribute :id, Types::Integer
     attribute :email, Types::String
     attribute :created_at, Types::Time
   end
   ```

4. **Add query methods** for reusable filters. Encapsulate all query filters inside public relation methods — keep business logic, validations, and side effects out of Relations:

   ```ruby
   def active
     where(status: "active")
   end

   def by_email(email)
     where(email: email)
   end
   ```

5. **Define associations**:
   Use the association DSL macros for basic and advanced linkage between tables:

   ```ruby
   associations do
     # Basic cases
     many_to_one :users, as: :author
     one_to_many :posts, as: :posts

     # Aliased foreign key
     many_to_one :users, foreign_key: :author_id, as: :author

     # Nested / through association
     one_to_many :comments, through: :posts
   end
   ```

   **Loading strategies:** use `combine` to eagerly load associations in a single composed query, or `load` to issue a separate query:

   ```ruby
   # In a repository or relation method:
   users.combine(:posts)           # eager-load posts into each user struct
   users.combine(posts: :comments) # nested eager-load
   ```

6. **Keep Relations in sync with migrations**:
   If not using `infer: true`, manually sync explicit schema declarations when migrations alter the database. Verify using the console:

   ```bash
   bundle exec hanami console
   # check app["relations.users"].schema
   ```

   **If schema mismatch or boot failure:** check migration status → run pending migrations (`bundle exec hanami db migrate`) → restart the console → re-verify the schema. If `infer: true` fails at boot, confirm the table exists in the database and that the connection configuration is correct.

---

## Common Mistakes

| Mistake | Resolution |
|---|---|
| Redundant declarations | Do not declare explicit attributes for columns that are already auto-inferred (causes boot errors). |
| Direct View access | Bypassing the Repository layer to query relations directly in views is an anti-pattern. |
| Singular class names | Relations map to plural database tables and must be plural: `Users`, not `User`. |

---

> For detailed relation pattern examples, see [RELATIONS.md](RELATIONS.md).

## Integration

| Related Skill | When to chain |
|---|---|
| **write-migration** | After any migration that changes the schema — verify Relation still matches |
| **create-repository** | When you need domain-level read/write operations that wrap Relations |
| **define-entity** | When defining the data structures returned by Relations |
| **write-rom-spec** (testing) | When writing tests for Relation query methods |
| **build-crud-resource** | Full end-to-end: Relation → Repository → Action → View → Test |

