Page updated Jan 16, 2024

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

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 in amplify/cli.json to true.
  • 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
  • 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 to name/_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.

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.
      • 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.
    • Screenshot visually showing the @key directive migration
  • 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.
    • Screenshot visually showing the default authorization mode 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.
    • Screenshot visually showing the original owner authorization rule and the migrated rule with deny by default
  • 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.
  • 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.