Page updated Jan 16, 2024

Evolving GraphQL schemas

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:

1type Todo @model {
2 id: ID!
3 title: String!
4}

becomes:

1type Task @model @mapsTo(name: "Todo") {
2 id: ID!
3 title: String!
4}

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.

  • @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.

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:

1type Post @model {
2 id: ID!
3 title: String!
4 comments: [Comment] @hasMany
5}
6
7type Comment @model {
8 id: ID!
9 message: String!
10 # postCommentsId: String is an autogenerated field containing the foreign key
11}

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:

1type Article @model @mapsTo(name: "Post") {
2 id: ID!
3 title: String!
4 comments: [Comment] @hasMany
5}
6
7type Comment @model {
8 id: ID!
9 message: String!
10 # articleCommentsId: String is the new autogenerated field containing the foreign key
11}

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:

1type Post @model {
2 id: ID!
3 title: String!
4 comments: [Reaction] @hasMany # this field cannot be renamed and still access existing relationship data
5}
6
7type Reaction @model @mapsTo(name: "Comment") {
8 id: ID!
9 message: String!
10 # autogenerated field postCommentsId: String contains the foreign key
11}

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:

1type Article @model @mapsTo(name: "Post") {
2 id: ID!
3}
4
5type Post @model {
6 id: ID!
7}

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.