GraphQL Transformer v1 to v2 migration
With the GraphQL Transformer v2, Amplify introduces as a range of new features to help customers create app backends even faster and easier.
What is changing?
With the new GraphQL Transformer v2, table index configuration, authorization rules, entity relationships, and OpenSearch integration are changing. Also, at the core of the new GraphQL Transformer is a new architecture leveraging AWS AppSync Pipeline Resolvers.
Table index configuration changes:
- The
@key
directive is being replaced by two new directives. - Customers can now specify
@primaryKey
on a field to define it as the primary key of a table. Customers can also specify sort key fields via a directive argument. - Customers can now specify
@index
on a field to use it as the partition key for a DynamoDB Global Secondary Index. Customers can optionally also specify a queryField or sort key fields by passing additional parameters. - The
@versioned
directive is deprecated in favor of AppSync’s built-in conflict detection and resolution feature.
Authorization rule changes:
- Prior to this migration, Amplify CLI will automatically allow access to all data with the default authorization type (API Key, UserPools, OIDC only) unless otherwise specified. With the new GraphQL Transformer, all data access is deny-by-default and customers need to explicitly specify authorization rules to enable clients to access their data.
- Prior to this migration, when customers used owner-based authorization
@auth(rules: [{allow: owner, operations: [read, update, delete]}])
, the “operations” fields were used to deny others access to the listed operations. In this example: “others can’t read, update, or delete.” With the new GraphQL Transformer, given the new deny-by-default paradigm, the owner-based authorization’s operation now specifies what owners are allowed to do. The same example above now means: “Owners can read, update, and delete”. - Prior to this migration, when customers specified an
@auth
rule on a field, the authorization was also applied on the model. With the new GraphQL Transformer,@auth
rules specified on a field will override all authorization rules defined at the model level. If some model level authorization rules need to be applied on a field level, you need to explicitly specify them.
Relationship mapping/connection changes:
@connection
is being replaced with several new directives.@hasOne
creates a “one-to-one” relationship with the target model.@hasMany
creates a "one-to-many" relationship with the target model.@belongsTo
facilitates a bi-directional relationship between two models. For example, use the@belongsTo
directive on a@hasMany
-relationship’s target model to create a relationship back to the source model.@manyToMany
creates a new relationship cardinality for Amplify’s GraphQL Transformer. Traditionally, customers had to create a “join table” between two models and create hasMany relationships from both models into that join table as a work around for this feature. With the new transformer, customers can specify a@manyToMany
relationship between the models and Amplify CLI will create the join tables behind the scenes.- Note: if you want to store additional properties on the join table, or if you have an existing join table, you can continue to use two
@hasMany
<=>@belongsTo
relationships to facilitate a many-to-many relationship. - Note: To prevent DataStore synchronization errors when using Table Joins, remove the
queries: null
from@model(queries: null)
in your GraphQL Schema. - Note: if you want to store additional properties on the join table, or if you have an existing join table, you can continue to use two
@hasMany
<=>@belongsTo
relationships to facilitate a many-to-many relationship
- Note: if you want to store additional properties on the join table, or if you have an existing join table, you can continue to use two
OpenSearch integration changes:
- DataStore-enabled GraphQL APIs now support OpenSearch integrations.
- Aggregation queries such as (min, max, sum, avg) and total counts are now available and scoped based on the user access permissions
- OpenSearch version 7.10 is now the new default version.
- Backfill batch size has been increased to 100 which will bring down the cost of backfill and allows faster backfill of existing data.
- Customers can now sort their OpenSearch search results via multiple fields to better refine their search queries.
What are the breaking changes and how do I migrate?
Changes that Amplify CLI requires you to manually migrate
- I use a Relational Database data source with the GraphQL API
- Currently, the new GraphQL Transformer does not support relational data sources. You can remain on the current GraphQL Transformer version and suppress further migration warnings by changing the
suppressschemamigrationprompt
feature flag inamplify/cli.json
totrue
.
- Currently, the new GraphQL Transformer does not support relational data sources. You can remain on the current GraphQL Transformer version and suppress further migration warnings by changing the
- I use a custom resolver in my GraphQL API
- To add a custom resolver in the new GraphQL Transformer follow the instructions here.
- I have overwritten Amplify-generated resolvers by placing custom .vtl files in the resolvers/ folder.
- In GraphQL Transformer v2, all resolvers are “pipeline resolvers”, this means we provide you more modularity and flexibility when customizing the auto-generated resolvers.
- First, review all the unit resolvers within the
build/resolvers/
folder and identify the unit resolver VTL file(s) that you’d like to overwrite. - Second, rename your VTL files from the resolvers/ folder based on the one you’d like to overwrite in the
build/resolvers/
folder. Your VTL filenames should follow this naming convention:{Query|Mutation}.{Model}.{Field}.{Slot}.{Position}.{req|res}.vtl
where “Slot” is:- One of init, preAuth, auth, postAuth, preDataLoad for request mapping templates.
- One of postDataLoad, finish for response mapping templates.
- Review how to extend Amplify-generated resolvers
- First, review all the unit resolvers within the
- In GraphQL Transformer v2, all resolvers are “pipeline resolvers”, this means we provide you more modularity and flexibility when customizing the auto-generated resolvers.
- I have used
@searchable
in my GraphQL schema. When migrating to the new GraphQL Transformer, the OpenSearch domain will be replaced with OpenSearch version 7.10 which may result in data loss due to the breaking changes (https://docs.aws.amazon.com/opensearch-service/latest/developerguide/version-migration.html) in OpenSearch from version 6.x.x.- To preserve the OpenSearch domain data, manually take a snapshot of the opensearch domain, migrate to v2 transformer and restore the snapshot as described here.
- I use auto generated searchable queries
- Clients using OpenSearch version >= 1.1 will need to modify their searchable resolvers using the resolver override. Clients using version >= 1.1 will need to change the indexPath of the query from
name/doc/_search
toname/_search
. Without this, all autogenerated searchable queries will fail. You can easily do this by copying the built templates and moving them to a resolver folder on the same level as the build folder.
- Clients using OpenSearch version >= 1.1 will need to modify their searchable resolvers using the resolver override. Clients using version >= 1.1 will need to change the indexPath of the query from
Changes that Amplify CLI will auto-migrate for you
To help you migrate to the GraphQL transformer v2, amplify migrate api
has been added to Amplify CLI. This tool will automatically migrate many schema scenarios for you. It is recommended to test your migrated schema in a development environment. Read on to learn about how to manually migrate your schema or to double check the output of the migration tool for correctness.
@key
directive is being deprecated and replaced with@primaryKey
and@index
.- Migration: The new
@primaryKey
and@index
directives fully replace the@key
functionality and provide a more intuitive interface.- To migrate primary keys:
- Remove the
@key
directive from the model. - Add the
@primaryKey
directive to the primary key’s field. - If sort keys are used: Provide the sort key field names as an array to the
@primaryKey
‘s sortKeyFields parameter.
- Remove the
- To migrate GSIs:
- Remove the
@key
directive from the model. - Add the
@index
directive to the field and supply the name parameter with the@key
directive’s keyName parameter. - If sort keys are used: Provide the sort key field names as an array to the
@index
’s sortKeyFields parameter. - If a custom queryField is used: Provide the queryField name via the
@index
’s queryField parameter.
- Remove the
- To migrate primary keys:
- Migration: The new
- The GraphQL API's authorization is now “deny by default”, meaning that clients cannot access the stored data unless an authorization rule is specified.
- Migration:
- For
@model
types that don’t have an authorization rule configured based on your GraphQL API's default authorization mode, you now have to explicitly set the@auth
rule using the default authorization mode.
- For
- Migration:
@auth
’s owner-based authorization will be configured with an “allow”-based model instead of a “deny-based” system- Migration: First, identify if you have omitted any “operations” from the “operations” parameter. In transformer v1, the specified operations are the ones that “others“ are denied access to. In transformer v2, apply the omitted operations to the ”private“ rule.
- Field-level authorization rules need to be rewritten to explicitly specify who is granted which type of access for a given field. A field rule will override any existing object rules. To maintain the object rule it needs to be specified again on the field. If a rule is only listed on the field and not the object, access is denied to all other fields.
- Some directives implicitly create fields. These fields cannot utilize field-level authorization because they are not specified in the input schema.
- In v1 of the GraphQL Transformer, when combining
@searchable
with@auth
, the total count in a search result contained records that the user was not authorized to access. In v2 of the GraphQL Transformer, the total count only contains results that the user is authorized to access. Similarly, to run an aggregation on a field, the user must have read access to that field. @connection
is being deprecated in favor of more explicit directives detailing the relation’s semantics:@hasOne
,@hasMany
,@belongsTo
,@manyToMany
- Migration: First, identify all your connections and their respective relationship types: “has one”, “has many”, “belongs to”, or “many to many”.
- To migrate “has one” relationships, replace the
@connection
directive with@hasOne
. If needed, provide the fields parameter with the fields from the@connection
directive. - To migrate “has many” relationships, replace the
@connection
directive with@hasMany
. Provide the keyName and fields values from the@connection
directive as the indexName and fields parameters in the@hasMany
directive. - To migrate “belongs to” relationships, replace the
@connection
directive with@belongsTo
. Specify the fields values from the@connection
directive as the fields parameter in the@belongsTo
directive. - An existing many-to-many relationship cannot use the new
@manyToMany
directive because the underlying join table and index names are not the same as the existing ones. The@connection
directives composing a many-to-many relationship should be replaced with their@hasMany
and@belongsTo
complements based on the above rules. Newly created many-to-many relationships can leverage the@manyToMany
directive to simplify the definition of this relationship type.
- To migrate “has one” relationships, replace the
- Migration: First, identify all your connections and their respective relationship types: “has one”, “has many”, “belongs to”, or “many to many”.
- The
improvePluralization
feature flag is not respected by the new GraphQL Transformer. The new Transformer always uses improved pluralization to create list queries. If your project does not have this Feature Flag enabled and you have a list query that is affected by the flag (such as listCactuss vs listCacti), then you will need to migrate your application code to use the new list query names. - The
validateTypeNameReservedWords
feature flag is not respected by the new GraphQL Transformer. The new Transformer always throws an error if a type name is a reserved word.