Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.

Page updated Apr 29, 2024

Relational models

The @hasOne and @hasMany directives do not support referencing a model which then references the initial model via @hasOne or @hasMany if DataStore is enabled.

DataStore has the capability to handle relationships between Models, such as has one, has many, belongs to. In GraphQL this is done with the @hasOne, @hasMany and @index directives as defined in the GraphQL Transformer documentation.

Updated schema

For the examples below with DataStore let's add a new model to the sample schema:

enum PostStatus {
ACTIVE
INACTIVE
}
type Post @model {
id: ID!
title: String!
rating: Int!
status: PostStatus!
# New field with @hasMany
comments: [Comment] @hasMany
}
# New model
type Comment @model {
id: ID!
post: Post @belongsTo
content: String!
}

Saving relations

In order to save connected models, you will create an instance of the model you wish to connect and pass its ID to DataStore.save:

const post = await DataStore.save(
new Post({
title: 'My Post with comments',
rating: 10,
status: PostStatus.ACTIVE
})
);
await DataStore.save(
new Comment({
content: 'Loving Amplify DataStore!',
post: post
})
);

Querying relations

You can query related models in three ways:

  • Option 1: use the toArray() function to lazy load related comments
const post = await DataStore.query(Post, "YOUR_POST_ID")
if (post) {
const comments = await post.comments.toArray()
}
const post = await DataStore.query(Post, "YOUR_POST_ID");
const comments = await post.comments.toArray();
  • Option 2: use async iterators to lazy load related comments
const post = await DataStore.query(Post, "YOUR_POST_ID");
if (post) {
for await (const comment of post.comments) {
console.log(comment)
};
}
const post = await DataStore.query(Post, "YOUR_POST_ID");
for await (const comment of post.comments) {
console.log(comment)
};
  • Option 3: use nested query predicates with the Comment model
const comments = await DataStore.query(Comment, c => c.post.id.eq('YOUR_POST_ID'));

Deleting relations

When you delete a parent object in a one-to-many relationship, the children will also be removed from the DataStore and mutations for this deletion will be sent over the network. For example, the following operation would remove the Post with id 123 as well as any related comments:

const toDelete = await DataStore.query(Post, "123");
if (toDelete) {
DataStore.delete(toDelete);
}
const toDelete = await DataStore.query(Post, "123");
DataStore.delete(toDelete);

However, in a many-to-many relationship the children are not removed and you must explicitly delete them.

Many-to-many

For many-to-many relationships, you can use the @manyToMany directive and specify a relationName. Under the hood, Amplify creates a join table and a one-to-many relationship from both models.

enum PostStatus {
ACTIVE
INACTIVE
}
type Post @model {
id: ID!
title: String!
rating: Int
status: PostStatus
editors: [User] @manyToMany(relationName: "PostEditor")
}
type User @model {
id: ID!
username: String!
posts: [Post] @manyToMany(relationName: "PostEditor")
}
// first you save the post
const post = await DataStore.save(
new Post({
title: 'My first post'
})
);
// secondly, you save the editor/user
const editor = await DataStore.save(
new User({
username: 'Nadia'
})
);
// then you save the model that links a post with an editor
await DataStore.save(
new PostEditor({
post: post,
user: editor
})
);