Relationships — belongsTo and hasMany

Declare Once, Resolved Async

Relationships — belongsTo and hasMany

The belongsTo and hasMany decorators declare relationships on a model. Ember Data resolves them lazily by default.

4 min read Level 3/5 #ember#ember-data#relationships
What you'll learn
  • Define belongsTo and hasMany relationships
  • Use the async option for lazy loading
  • Use the inverse option to disambiguate

Ember Data models relationships declaratively. You say “a post has many comments and belongs to an author”, and the store handles loading, caching, and updating the relation.

belongsTo and hasMany

import Model, { attr, belongsTo, hasMany } from '@ember-data/model';

export default class PostModel extends Model {
  @attr('string') title;

  @belongsTo('author', { async: true, inverse: 'posts' })
  author;

  @hasMany('comment', { async: true, inverse: 'post' })
  comments;
}

'author' and 'comment' are model type strings — they match the model filenames.

async vs sync

When async: true, the relation returns a proxy you await:

const author = await post.author;
const comments = await post.comments;

When async: false, the relation must be present in the same payload as the parent (side-loaded via JSON-API included), and you read it synchronously:

post.author.name; // no await

Sync relationships are great for tight, prefetched graphs; async for sprawling, lazy ones.

inverse

If two models reference each other once, Ember Data infers the inverse. If there are multiple links (e.g., a post has author and editor both pointing at user), you must pass inverse explicitly:

@belongsTo('user', { async: true, inverse: 'authoredPosts' })
author;

@belongsTo('user', { async: true, inverse: 'editedPosts' })
editor;

Use inverse: null if a relationship has no opposite side on the other model.

Mutating Relationships

const comments = await post.comments;
comments.push(newComment);
await post.save();

For belongsTo, just assign: post.author = otherAuthor; await post.save().

The Store →