Skip to content

Relationships: One to One ​

One-to-one is the right fit when exactly one related record should exist on either side of the association. Typical examples are User -> Profile, Order -> Receipt, or Post -> SeoEntry.

hasOne ​

Use hasOne on the parent-facing side when the related table stores the foreign key.

ts
const User = defineModel('users', {
  relations: {
    profile: hasOne('Profile', {
      foreignKey: 'user_id',
    }),
  },
})

String relation targets use the model name inferred from the related table, including irregular plurals such as people -> Person and children -> Child.

ts
await user.profile().create({
  locale: 'en',
  timezone: 'UTC',
})

Query through the relation ​

ts
await user.load('profile')

const profile = user.getRelation('profile')

belongsTo ​

Use belongsTo on the inverse side when the current model stores the foreign key.

ts
const Profile = defineModel('profiles', {
  relations: {
    user: belongsTo('User', {
      foreignKey: 'user_id',
    }),
  },
})
ts
const profile = await Profile.findOrFail(1)

await profile.load('user')

const user = profile.getRelation('user')

Eager load the inverse ​

ts
const profiles = await Profile.with('user').get()

Constrain by the parent ​

ts
const profiles = await Profile.whereBelongsTo(user, 'user').get()

Associate and dissociate ​

ts
profile.user().associate(user)
await profile.save()

profile.user().dissociate()
await profile.save()

One-to-one workflow example ​

Use a one-to-one relation when the dependent record belongs to one parent and should not be modeled as a collection.

ts
const user = await User.create({
  name: 'Amina',
  email: '[email protected]',
})

await user.profile().create({
  locale: 'en',
  timezone: 'UTC',
})

Practical notes ​

  • define hasOne on the parent side
  • define belongsTo on the record that stores the foreign key
  • use eager loading when the response needs both sides
  • use associate(...) and dissociate(...) when the inverse side already exists

Holo owns backend runtime concerns. The host framework owns SSR and routing.