export const meta = {
  title: `Add relationships between types`,
  description: `Define relationships with other types in your schema.`,
};

<MigrationAlert isLegacy url={"/cli/graphql/data-modeling"}/>

## @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
              }
            }
          }
        }
      }
    }
  }
}
```
<Callout warning>
Amplify Studio does not support custom naming. Changing the auto-generated name will break Amplify Studio.
</Callout>

#### 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.
