Page updated Jan 16, 2024

Relational models

Amplify Flutter v0 is now in Maintenance Mode until July 19th, 2024. This means that we will continue to include updates to ensure compatibility with backend services and security. No new features will be introduced in v0.

Please use the latest version (v1) of Amplify Flutter to get started.

If you are currently using v0, follow these instructions to upgrade to v1.

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 schema.graphql file located by default at amplify/backend/{api_name}/:

1enum PostStatus {
2 ACTIVE
3 INACTIVE
4}
5
6type Post @model {
7 id: ID!
8 title: String!
9 rating: Int!
10 status: PostStatus!
11 # New field with @hasMany
12 comments: [Comment] @hasMany(indexName: "byPost", fields: ["id"])
13}
14
15# New model
16type Comment @model {
17 id: ID!
18 postID: ID! @index(name: "byPost")
19 post: Post! @belongsTo(fields: ["postID"])
20 content: String!
21}

After that, regenerate the models with the following console command:

1amplify codegen models

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:

1Future<void> savePostAndComment() async {
2 final post = Post(
3 title: 'My Post with comments',
4 rating: 10,
5 status: PostStatus.ACTIVE,
6 );
7 final comment = Comment(
8 post: post, // Directly pass in the post instance
9 content: 'Loving Amplify DataStore!',
10 );
11
12 await Amplify.DataStore.save(post);
13 print('Post saved');
14 await Amplify.DataStore.save(comment);
15 print('Comment saved');
16}

Querying relations

In order to retrieve all models that are related to a parent model, you can use query predicates with the query API:

1Future<void> fetchAllCommentsForPostId(String postId) async {
2 final comments = await Amplify.DataStore.query(
3 Comment.classType,
4 where: Comment.POST.eq(postId),
5 );
6 print('Comments: $comments');
7}

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:

1Future<void> deletePostWithId(String id) async {
2 final postsWithId = await Amplify.DataStore.query(
3 Post.classType,
4 where: Post.ID.eq(id),
5 );
6
7 for (final element in postsWithId) {
8 await Amplify.DataStore.delete(element);
9 print('Deleted a post');
10 }
11}

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.

1enum PostStatus {
2 ACTIVE
3 INACTIVE
4}
5
6type Post @model {
7 id: ID!
8 title: String!
9 rating: Int
10 status: PostStatus
11 editors: [User] @manyToMany(relationName: "PostEditor")
12}
13
14type User @model {
15 id: ID!
16 username: String!
17 posts: [Post] @manyToMany(relationName: "PostEditor")
18}

Add the model above to the schema.graphql file located by default at amplify/backend/{api_name}/ and regenerate the models again with the following command:

1amplify codegen models

Once it is regenerated, save your posts with many-to-many mode like the following:

1Future<void> savePostAndEditor() async {
2 final post = Post(title: 'My First Post');
3 final editor = User(username: 'Nadia');
4 final postEditor = PostEditor(post: post, user: editor);
5
6 // first you save the post
7 await Amplify.DataStore.save(post);
8
9 // secondly, you save the editor/user
10 await Amplify.DataStore.save(editor);
11
12 // then you save the model that links a post with an editor
13 await Amplify.DataStore.save(postEditor);
14 print('Saved user, post and postEditor!');
15}