export const meta = { title: `Add relationships between types`, description: `Define relationships with other types in your schema.`, }; ## @connection The `@connection` directive enables you to specify relationships between `@model` types. Currently, this supports one-to-one, one-to-many, and many-to-one relationships. You may implement many-to-many relationships using two one-to-many connections and a joining `@model` type. See the usage section for details. [We also provide a fully working schema with 17 patterns related to relational designs](/cli/graphql-transformer/dataaccess). ### Definition ```graphql directive @connection(keyName: String, fields: [String!]) on FIELD_DEFINITION ``` ### Usage Relationships between types are specified by annotating fields on an `@model` object type with the `@connection` directive. The `fields` argument can be provided and indicates which fields can be queried by to get connected objects. The `keyName` argument can optionally be used to specify the name of secondary index (an index that was set up using `@key`) that should be queried from the other type in the relationship. When specifying a `keyName`, the `fields` argument should be provided to indicate which field(s) will be used to get connected objects. If `keyName` is not provided, then `@connection` queries the target table's primary index. ### Has one In the simplest case, you can define a one-to-one connection where a project has one team: ```graphql type Project @model { id: ID! name: String team: Team @connection } type Team @model { id: ID! name: String! } ``` You can also define the field you would like to use for the connection by populating the first argument to the fields array and matching it to a field on the type: ```graphql type Project @model { id: ID! name: String teamID: ID! team: Team @connection(fields: ["teamID"]) } type Team @model { id: ID! name: String! } ``` In this case, the Project type has a `teamID` field added as an identifier for the team that the project belongs to. `@connection` can then get the connected Team object by querying the Team table with this `teamID`. After it's transformed, you can create projects and query the connected team as follows: ```graphql mutation CreateProject { createProject(input: { name: "New Project", teamID: "a-team-id"}) { id name team { id name } } } ``` > **Note:** The **Project.team** resolver is configured to work with the defined connection. This is done with a query on the Team table where `teamID` is passed in as an argument. A Has One @connection can only reference the primary index of a model (ie. it cannot specify a "keyName" as described below in the Has Many section). ### Has many The following schema defines a Post that can have many comments: ```graphql type Post @model { id: ID! title: String! comments: [Comment] @connection(keyName: "byPost", fields: ["id"]) } type Comment @model @key(name: "byPost", fields: ["postID", "content"]) { id: ID! postID: ID! content: String! } ``` Note how a one-to-many connection needs an `@key` that allows comments to be queried by the postID and the connection uses this key to get all comments whose postID is the id of the post was called on. After it's transformed, you can create comments and query the connected Post as follows: ```graphql mutation CreatePost { createPost(input: { id: "a-post-id", title: "Post Title" } ) { id title } } mutation CreateCommentOnPost { createComment(input: { id: "a-comment-id", content: "A comment", postID: "a-post-id"}) { id content } } ``` And you can query a Post with its comments as follows: ```graphql query getPost { getPost(id: "a-post-id") { id title comments { items { id content } } } } ``` ### Belongs to You can make a connection bi-directional by adding a many-to-one connection to types that already have a one-to-many connection. In this case you add a connection from Comment to Post since each comment belongs to a post: ```graphql type Post @model { id: ID! title: String! comments: [Comment] @connection(keyName: "byPost", fields: ["id"]) } type Comment @model @key(name: "byPost", fields: ["postID", "content"]) { id: ID! postID: ID! content: String! post: Post @connection(fields: ["postID"]) } ``` After it's transformed, you can create comments with a post as follows: ```graphql mutation CreatePost { createPost(input: { id: "a-post-id", title: "Post Title" } ) { id title } } mutation CreateCommentOnPost1 { createComment(input: { id: "a-comment-id-1", content: "A comment #1", postID: "a-post-id"}) { id content } } mutation CreateCommentOnPost2 { createComment(input: { id: "a-comment-id-2", content: "A comment #2", postID: "a-post-id"}) { id content } } ``` And you can query a Comment with its Post, then all Comments of that Post by navigating the connection: ```graphql query GetCommentWithPostAndComments { getComment( id: "a-comment-id-1" ) { id content post { id title comments { items { id content } } } } } ``` ### Many-to-many connections You can implement many to many using two 1-M @connections, an @key, and a joining @model. For example: ```graphql type Post @model { id: ID! title: String! editors: [PostEditor] @connection(keyName: "byPost", fields: ["id"]) } # Create a join model and disable queries as you don't need them # and can query through Post.editors and User.posts type PostEditor @model(queries: null) @key(name: "byPost", fields: ["postID", "editorID"]) @key(name: "byEditor", fields: ["editorID", "postID"]) { id: ID! postID: ID! editorID: ID! post: Post! @connection(fields: ["postID"]) editor: User! @connection(fields: ["editorID"]) } type User @model { id: ID! username: String! posts: [PostEditor] @connection(keyName: "byEditor", fields: ["id"]) } ``` This case is a bidirectional many-to-many which is why two `@key` calls are needed on the PostEditor model. You can first create a Post and a User, and then add a connection between them with by creating a PostEditor object as follows: ```graphql mutation CreateData { p1: createPost(input: { id: "P1", title: "Post 1" }) { id } p2: createPost(input: { id: "P2", title: "Post 2" }) { id } u1: createUser(input: { id: "U1", username: "user1" }) { id } u2: createUser(input: { id: "U2", username: "user2" }) { id } } mutation CreateLinks { p1u1: createPostEditor(input: { id: "P1U1", postID: "P1", editorID: "U1" }) { id } p1u2: createPostEditor(input: { id: "P1U2", postID: "P1", editorID: "U2" }) { id } p2u1: createPostEditor(input: { id: "P2U1", postID: "P2", editorID: "U1" }) { id } } ``` Note that neither the User type nor the Post type have any identifiers of connected objects. The connection info is held entirely inside the PostEditor objects. You can query a given user with their posts: ```graphql query GetUserWithPosts { getUser(id: "U1") { id username posts { items { post { title } } } } } ``` Also you can query a given post with the editors of that post and can list the posts of those editors, all in a single query: ```graphql query GetPostWithEditorsWithPosts { getPost(id: "P1") { id title editors { items { editor { username posts { items { post { title } } } } } } } } ``` Amplify Studio does not support custom naming. Changing the auto-generated name will break Amplify Studio. #### Alternative definition The above definition is the recommended way to create relationships between model types in your API. This involves defining index structures using `@key` and connection resolvers using `@connection`. There is an older parameterization of `@connection` that creates indices and connection resolvers that is still functional for backwards compatibility reasons. It is recommended to use `@key` and the new `@connection` via the fields argument. ```graphql directive @connection(name: String, keyField: String, sortField: String, limit: Int) on FIELD_DEFINITION ``` This parameterization is not compatible with `@key`. See the parameterization above to use `@connection` with indexes created by @key. ### Limit The default number of nested objects is 100. You can override this behavior by setting the **limit** argument. For example: ```graphql type Post @model { id: ID! title: String! comments: [Comment] @connection(limit: 50) } type Comment @model { id: ID! content: String! } ``` ### Generates In order to keep connection queries fast and efficient, the GraphQL transform manages global secondary indexes (GSIs) on the generated tables on your behalf when using @connection > **Note:** After you have pushed a `@connection` directive you should not try to change it. If you try to change it, the DynamoDB UpdateTable operation will fail. Should you need to change a `@connection`, you should add a new `@connection` that implements the new access pattern, update your application to use the new `@connection`, and then delete the old `@connection` when it's no longer needed.