aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.43k stars 2.13k forks source link

Issue: GraphQL Mutations Resolver return #13685

Closed zainab404 closed 3 months ago

zainab404 commented 3 months ago

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

GraphQL API

Amplify Version

Older than v5

Amplify Categories

api

Backend

None

Environment information

``` # Put output below this line System: OS: macOS 14.5 CPU: (8) arm64 Apple M1 Memory: 59.53 MB / 16.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 18.16.0 - ~/.nvm/versions/node/v18.16.0/bin/node Yarn: 1.22.10 - /usr/local/bin/yarn npm: 9.5.1 - ~/.nvm/versions/node/v18.16.0/bin/npm Browsers: Chrome: 127.0.6533.89 Safari: 17.5 npmPackages: @ampproject/toolbox-optimizer: undefined () @aws-amplify/ui-react: ^2.20.0 => 2.20.0 @aws-amplify/ui-react-internal: undefined () @aws-amplify/ui-react-legacy: undefined () @babel/core: undefined () @babel/runtime: 7.15.4 @edge-runtime/primitives: 2.0.0 @emotion/react: ^11.9.3 => 11.9.3 @emotion/server: ^11.4.0 => 11.4.0 @emotion/styled: ^11.9.3 => 11.9.3 @faker-js/faker: ^7.3.0 => 7.3.0 @fontsource/roboto: ^4.5.7 => 4.5.7 @hapi/accept: undefined () @mapbox/mapbox-gl-style-spec: 13.25.0 (13.18.0-dev) @mui/icons-material: ^5.8.4 => 5.8.4 @mui/lab: ^5.0.0-alpha.120 => 5.0.0-alpha.120 @mui/material: ^5.11.10 => 5.11.10 @mui/x-date-pickers: ^5.0.0-beta.4 => 5.0.0-beta.4 @mui/x-date-pickers-pro: ^5.0.0-beta.4 => 5.0.0-beta.4 @napi-rs/triples: undefined () @next/react-dev-overlay: undefined () @react-pdf/renderer: ^3.1.9 => 3.1.9 @reduxjs/toolkit: ^1.8.3 => 1.8.3 @reduxjs/toolkit-query: 1.0.0 @reduxjs/toolkit-query-react: 1.0.0 @segment/ajv-human-errors: undefined () @strapi/blocks-react-renderer: ^1.0.0 => 1.0.0 @vercel/nft: undefined () acorn: undefined () amphtml-validator: undefined () anser: undefined () arg: undefined () assert: undefined () async-retry: undefined () async-sema: undefined () autosuggest-highlight: ^3.3.4 => 3.3.4 aws-amplify: ^4.3.24 => 4.3.24 axios: ^1.6.8 => 1.6.8 (0.21.4) babel-packages: undefined () babel-plugin-jsx-control-statements: ^4.1.2 => 4.1.2 browserify-zlib: undefined () browserslist: undefined () buffer: undefined () bytes: undefined () chalk: undefined () chrome-aws-lambda: ^10.1.0 => 10.1.0 ci-info: undefined () cli-select: undefined () comment-json: undefined () compression: undefined () conf: undefined () constants-browserify: undefined () content-disposition: undefined () content-type: undefined () cookie: undefined () cross-spawn: undefined () crypto-browserify: undefined () css.escape: undefined () cssnano-simple: undefined () data-uri-to-buffer: undefined () date-fns: ^2.29.1 => 2.29.1 debug: undefined () devalue: undefined () domain-browser: undefined () edge-runtime: undefined () empty-npm-package: undefined (1.0.0) eslint: 8.16.0 => 8.16.0 eslint-config-next: 12.1.6 => 12.1.6 events: undefined () final-form: ^4.20.7 => 4.20.7 final-form-arrays: ^3.0.2 => 3.0.2 find-cache-dir: undefined () find-up: undefined () fresh: undefined () get-orientation: undefined () glob: undefined () gzip-size: undefined () html-react-parser: ^5.0.6 => 5.0.6 http-proxy: undefined () https-browserify: undefined () icss-utils: undefined () ignore-loader: undefined () image-size: undefined () immutability-helper: ^3.1.1 => 3.1.1 is-animated: undefined () is-docker: undefined () is-wsl: undefined () jest-worker: undefined () json5: undefined () jsonwebtoken: undefined () loader-utils: undefined () lodash: ^4.17.21 => 4.17.21 lodash.curry: undefined () logrocket: ^6.0.1 => 6.0.1 lru-cache: undefined () mapbox-gl: ^2.9.2 => 2.9.2 (1.13.3) micromatch: undefined () mini-css-extract-plugin: undefined () moment: ^2.29.3 => 2.29.3 nanoid: undefined () native-url: undefined () neo-async: undefined () next: ^13.0.5 => 13.0.5 next-api-og-image: ^4.3.0 => 4.3.0 node-fetch: undefined () node-html-parser: undefined () notistack: ^2.0.5 => 2.0.5 ora: undefined () os-browserify: undefined () p-limit: undefined () path-browserify: undefined () platform: undefined () postcss-flexbugs-fixes: undefined () postcss-modules-extract-imports: undefined () postcss-modules-local-by-default: undefined () postcss-modules-scope: undefined () postcss-modules-values: undefined () postcss-preset-env: undefined () postcss-safe-parser: undefined () postcss-scss: undefined () postcss-value-parser: undefined () process: undefined () punycode: undefined () querystring-es3: undefined () raw-body: undefined () react: ^18.2.0 => 18.2.0 (18.3.0-next-2655c9354-20221121) react-confetti: ^6.1.0 => 6.1.0 react-csv-reader: ^4.0.0 => 4.0.0 react-dnd: ^16.0.1 => 16.0.1 react-dnd-html5-backend: ^16.0.1 => 16.0.1 react-dom: ^18.2.0 => 18.2.0 (18.3.0-next-2655c9354-20221121) react-dropzone: ^14.2.3 => 14.2.3 react-final-form: ^6.5.9 => 6.5.9 react-final-form-arrays: ^3.1.4 => 3.1.4 react-image-crop: ^8.6.12 => 8.6.12 react-image-file-resizer: ^0.4.8 => 0.4.8 react-is: 18.2.0 react-number-format: ^5.3.4 => 5.3.4 react-pdf: ^8.0.2 => 8.0.2 react-pdf-html: ^1.1.18 => 1.1.18 react-quill: ^2.0.0 => 2.0.0 react-redux: ^8.0.2 => 8.0.2 react-refresh: 0.12.0 react-select: ^5.4.0 => 5.4.0 react-select-async-paginate: ^0.6.2 => 0.6.2 react-server-dom-webpack: undefined () react-use: ^17.5.0 => 17.5.0 recharts: ^2.5.0 => 2.5.0 redux-actions: ^2.6.5 => 2.6.5 redux-devtools-extension: ^2.13.9 => 2.13.9 redux-persist: ^6.0.0 => 6.0.0 redux-persist/integration/react: undefined () redux-promise-middleware: ^6.1.2 => 6.1.2 redux-thunk: ^2.4.1 => 2.4.1 regenerator-runtime: 0.13.4 sass-loader: undefined () scheduler: undefined () schema-utils: undefined () semver: undefined () send: undefined () setimmediate: undefined () shell-quote: undefined () source-map: undefined () stacktrace-parser: undefined () stream-browserify: undefined () stream-http: undefined () string-hash: undefined () string_decoder: undefined () strip-ansi: undefined () tar: undefined () terser: undefined () text-table: undefined () timers-browserify: undefined () tty-browserify: undefined () ua-parser-js: undefined () undici: undefined () unistore: undefined () unsplash-js: ^7.0.15 => 7.0.15 util: undefined () uuid: ^8.3.2 => 8.3.2 (3.4.0, 3.3.2) vm-browserify: undefined () watchpack: undefined () web-vitals: undefined () webpack: undefined () webpack-sources: undefined () ws: undefined () npmGlobalPackages: @aws-amplify/cli: 12.6.0 corepack: 0.17.0 npm: 9.5.1 ```

Describe the bug

I'm having an issue with a GraphQL mutation using amplify. The mutation itself completes successfully, but an error is thrown when the resolver tries to return the mutated item. It's fetching a connection, which returns null despite the connection existing and working in queries

Expected behavior

What's expected to happen, and what has been working for the last 2 years is that the connection fetches as expected (with it's existing values). Thus, the return of the mutated item does not throw any errors as all expected fields exist and aren't null for any required fields.

Reproduction steps

  1. Call 'updateItem'
  2. updateItem mutates successfully
  3. Error is thrown when resolver is returning mutated item due to relatedEntity being null

Code Snippet

// My Schema.

type Project
@model
@auth(rules: [
  { allow: private, operations: [create, read, update, delete] }
])
{
  id: ID!
  title: String!
  contactProjectsId: ID
  client: Contact @belongsTo(fields: ["contactProjectsId"])
}

type Contact
@model
@auth(rules: [
  { allow: private, operations: [create, read, update, delete] }
])
{
  id: ID!
  name: String!
  projects: [Project] @hasMany
}

Log output

``` { "logType": "GraphQLFieldRuntimeError", "fieldInError": true, "path": [ "updateItem", "relatedEntity", "id" ], "errors": [ "Cannot return null for non-nullable type: 'ID' within parent 'RelatedEntity' (/updateItem/relatedEntity/id)" ] } ```

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

Mutation from AppSync console image

My workaround has been to create custom mutations that do not fetch the connection, this allows for the mutation to run without throwing error.

Please let me know if any other information is needed, help is much appreciated :')

chrisbonifacio commented 3 months ago

Hi @zainab404 👋 thanks for raising this issue. If you are experiencing issues with the return value in mutation resolvers, you might be running into the behavior explained in a warning in our docs for relational data:

https://docs.amplify.aws/gen1/react/build-a-backend/graphqlapi/data-modeling/#setup-relationships-between-models

With versions of Amplify CLI @aws-amplify/cli@12.12.2 and API Category@aws-amplify/amplify-category-api@5.11.5, an improvement was made to how relational field data is handled in subscriptions when different authorization rules apply to related models in a schema. The improvement redacts the values for the relational fields, displaying them as null or empty, to prevent unauthorized access to relational data. This redaction occurs whenever it cannot be determined that the child model will be protected by the same permissions as the parent model.

Because subscriptions are tied to mutations and the selection set provided in the result of a mutation is then passed through to the subscription, relational fields in the result of mutations must be redacted.

If an authorized end-user needs access to the redacted relational field they should perform a query to read the relational data.

Additionally, subscriptions will inherit related authorization when relational fields are set as required. To better protect relational data, consider modifying the schema to use optional relational fields.

Based on the security posture of your application, you can choose to revert to the subscription behavior before this improvement was made.

To do so, use the subscriptionsInheritPrimaryAuth feature flag under graphqltransformer in the amplify/backend/cli.json file.

  • If enabled, subscriptions will inherit the primary model authorization rules for the relational fields.
  • If disabled, relational fields will be redacted in mutation response when there is a difference between auth rules between primary and related models.
zainab404 commented 3 months ago

Ah I see, that worked, thanks so much!