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 schema.graphql
file located by default at amplify/backend/{api_name}/
:
1enum PostStatus {2 ACTIVE3 INACTIVE4}5
6type Post @model {7 id: ID!8 title: String!9 rating: Int!10 status: PostStatus!11 # New field with @hasMany12 comments: [Comment] @hasMany(indexName: "byPost", fields: ["id"])13}14
15# New model16type 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
:
1import 'package:amplify_flutter/amplify_flutter.dart';2
3import 'models/ModelProvider.dart';4
5Future<void> savePostAndComment() async {6 final post = Post(7 title: 'My Post with comments',8 rating: 10,9 status: PostStatus.ACTIVE,10 );11 final comment = Comment(12 post: post, // associate the comment to the post13 content: 'Loving Amplify DataStore!',14 );15
16 try {17 // Make sure to safe the parent Post first18 await Amplify.DataStore.save(post);19 await Amplify.DataStore.save(comment);20 } on DataStoreException catch (e) {21 safePrint('Something went wrong querying posts: ${e.message}');22 }23}
Querying relations
In order to retrieve all models that are related to a parent model, you can use query predicates with the query API:
1import 'package:amplify_flutter/amplify_flutter.dart';2
3import 'models/ModelProvider.dart';4
5Future<void> fetchAllCommentsForPostId(String postId) async {6 try {7 final comments = await Amplify.DataStore.query(8 Comment.classType,9 where: Comment.POST.eq(postId),10 );11 safePrint('Comments: $comments');12 } on DataStoreException catch (e) {13 safePrint('Something went wrong querying posts: ${e.message}');14 }15}
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:
1import 'package:amplify_flutter/amplify_flutter.dart';2
3import 'models/ModelProvider.dart';4
5Future<void> deletePostWithID123AndItsComments(String id) async {6 try {7 final post = (await Amplify.DataStore.query(8 Post.classType,9 where: Post.ID.eq(id),10 ))11 .first;12
13 // DataStore also deletes all associated comments with this operation14 await Amplify.DataStore.delete(post);15 } on DataStoreException catch (e) {16 safePrint('Something went wrong deleting models: ${e.message}');17 }18}
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.
1enum PostStatus {2 ACTIVE3 INACTIVE4}5
6type Post @model {7 id: ID!8 title: String!9 rating: Int10 status: PostStatus11 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:
1import 'package:amplify_flutter/amplify_flutter.dart';2
3import 'models/ModelProvider.dart';4
5Future<void> savePostAndEditor() async {6 final post = Post(7 title: 'My First Post',8 rating: 10,9 status: PostStatus.INACTIVE,10 );11 final editor = User(username: 'Nadia');12 final postEditor = PostEditor(post: post, user: editor);13
14 try {15 // first you save the post16 await Amplify.DataStore.save(post);17
18 // secondly, you save the editor/user19 await Amplify.DataStore.save(editor);20
21 // then you save the model that links a post with an editor22 await Amplify.DataStore.save(postEditor);23 } on DataStoreException catch (e) {24 safePrint('Something went wrong saving model: ${e.message}');25 }26}