export const meta = { title: `GraphQL Transformer v1 to v2 migration`, description: ``, }; With the [GraphQL Transformer v2](/cli/graphql/overview), 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](https://docs.aws.amazon.com/appsync/latest/devguide/pipeline-resolvers.html). ### 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](/cli/graphql/custom-business-logic/#vtl-resolver). * 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](/cli/graphql/custom-business-logic/#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](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/managedomains-snapshots.html). * I use auto generated searchable queries * Clients using OpenSearch version >= 1.1 will need to modify their searchable resolvers using the resolver override (https://docs.amplify.aws/cli/graphql/custom-business-logic/#override-amplify-generated-resolvers). 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](/images/cli/graphql/key-migration.png) * 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](/images/cli/graphql/default-auth-mode-migration.png) * `@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](/images/cli/graphql/owner-auth-migration.png) * 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.