Relational models
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 modeltype 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 synced to cloud. 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 relationships
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 postconst post = await DataStore.save( new Post({ title: 'My first post' }));
// secondly, you save the editor/userconst editor = await DataStore.save( new User({ username: 'Nadia' }));
// then you save the model that links a post with an editorawait DataStore.save( new PostEditor({ post: post, user: editor }));
Create
In order to save a many-to-many relationship, create both model instance first and then save them to a new join model instance.
const post = await DataStore.save( new Post({ title: 'Amplify Weekly', rating: 10, status: PostStatus.ACTIVE }));
const user = await DataStore.save( new User({ username: 'rene' }));
const postEditor = await DataStore.save( new PostEditor({ post: post, user: user }));
Here we've first created a new Post
instance and a new User
instance. Then, saved those instances to a new instance of our join model PostEditor
.
Query
To query many-to-many relationships, use a nested predicate to filter the target model by the source model's id:
const editors = await DataStore.query(User, (u) => u.posts.post.id.eq(post.id));
Delete
Deleting the join model instance will not delete any source model instances.
await DataStore.delete(toBeDeletedPostEditor);
Both the Post
and the User
instances will not be deleted. Only the join model instances containing the link between a Post
and a User
.
Deleting a source model instance will also delete the join model instances containing the source model instance.
await DataStore.delete(toBeDeletedUser);
The toBeDeletedUser
User instance and all PostEditor
instances where user
is linked to toBeDeletedUser
will be deleted.