export const meta = {
  title: `Evolving GraphQL schemas`,
  description: `Evolve your GraphQL schema over time using the @mapsTo directive to retain tables while renaming models`,
};

GraphQL schemas change over the lifecycle of a project. Sometimes these changes include breaking API changes. One such change is renaming a model in the schema, which Amplify offers a way to do while retaining the underlying records for that model.

## Renaming models while retaining data

Amplify supports renaming models in a GraphQL schema by using the `@mapsTo` directive. 
Normally when renaming a model, Amplify will remove the underlying table for the model and create a new table with the new name. Once a table contains production data that cannot be deleted, `@mapsTo` can be used to specify the original name. Amplify will use the original name to ensure the underlying DynamoDB tables and other resources point to the existing data.
Other GraphQL API references to the model will use the new name.

For example, a schema such as:
```graphql
type Todo @model {
  id: ID!
  title: String!
}
```
becomes:
```graphql
type Task @model @mapsTo(name: "Todo") {
  id: ID!
  title: String!
}
```
Amplify will update all of the GraphQL operations and types to use the name Task, but the Task model will point to the table that Todo was originally using.

<Callout>

- `@mapsTo` cannot be used to point a model to an arbitrarily named table. It can only be used to point a renamed model to it's original name.
- `@mapsTo` can only be used on @model GraphQL types that are backed by a DynamoDB table.

</Callout>

When renaming a model that has relationships with other models, Amplify will automatically map auto-generated foreign key fields to their original name. For example, given:

```graphql
type Post @model {
  id: ID!
  title: String!
  comments: [Comment] @hasMany
}

type Comment @model {
  id: ID!
  message: String!
  # postCommentsId: String is an autogenerated field containing the foreign key
}
```
Amplify will automatically add a field named `postCommentsId` to the Comment model that contains the foreign key of the Post. If the Post type is renamed to Article:

```graphql
type Article @model @mapsTo(name: "Post") {
  id: ID!
  title: String!
  comments: [Comment] @hasMany
}

type Comment @model {
  id: ID!
  message: String!
  # articleCommentsId: String is the new autogenerated field containing the foreign key
}
```
The underlying table still contains records with `postCommentsId` as the foreign key field in the Comment table. In the new schema the foreign key field is now `articleCommentsId`.
Amplify is aware of this and will automatically map incoming requests with `articleCommentsId` to `postCommentsId` and do the reverse mapping for results.

## Limitations 

### Constraint on relationship field names with @mapsTo

In the above example if you renamed Comment to Reaction:
```graphql
type Post @model {
  id: ID!
  title: String!
  comments: [Reaction] @hasMany # this field cannot be renamed and still access existing relationship data
}

type Reaction @model @mapsTo(name: "Comment") {
  id: ID!
  message: String!
  # autogenerated field postCommentsId: String contains the foreign key
}
```
The `@hasMany` field `comments` cannot be renamed to `reactions`. This is because the foreign key field in Reaction uses the parent field name as part of the name. Amplify cannot determine the original name if this is changed.

If a model is renamed multiple times, the value specified in `@mapsTo` must be the _original_ name, not the previous name.

### Constraints to prevent naming conflicts

A model in the schema cannot have the same name as the name another type maps to. For example, the following schema is invalid:
```graphql
type Article @model @mapsTo(name: "Post") {
  id: ID!
}

type Post @model {
  id: ID!
}
```
This schema would create a conflict on the Post table.

Furthermore, even if the Post model is mapped to a different name, it is still not allowed. While this scenario technically does not pose a conflict, it is disallowed to prevent confusion.

If you are accessing the table of a renamed model directly (ie. without going through AppSync), your access patterns will need to be aware that foreign key fields of records in the database are not renamed. See "How it works" below.

## How it works
`@mapsTo` does not modify any existing tables or records. Instead, it points AppSync resolvers for the new name to the existing DynamoDB table for the original name.

To handle renamed autogenerated foreign key fields when using relational directives, Amplify adds additional AppSync pipeline resolvers before and after fetching data from the database.
The resolvers before the fetch map any occurrence of the renamed foreign keys in the request to the original name. Then the resolvers after the fetch map any occurrence of the original name to the current name before returning the result.