graphql-tools icon indicating copy to clipboard operation
graphql-tools copied to clipboard

Stitching: object field is null when all selections are delegated

Open hedgepigdaniel opened this issue 2 years ago • 0 comments

Issue workflow progress

Progress of the issue based on the Contributor Workflow

  • [ ] 1. The issue provides a reproduction available on Github, Stackblitz or CodeSandbox

    Make sure to fork this template and run yarn generate in the terminal.

    Please make sure the GraphQL Tools package versions under package.json matches yours.

  • [x] 2. A failing test has been provided: https://github.com/ardatan/graphql-tools/pull/5741
  • [ ] 3. A local solution has been provided
  • [ ] 4. A pull request is pending review

Describe the bug

When using schema stitching, if a field a: A is included in a query with selections a { x y z }, then a is incorrectly resolved to null if x, y, z are all extensions to the stitched schema, and resolved through delegation to a different schema than the subschema that resolves a.

To Reproduce Steps to reproduce the behavior:

  1. Create two subschemas A and B, where A contains an object type aNamespace
  2. Stitch the two schemas together, and add an extensions that adds a field to aNamespace which resolves to a value in schema B
  3. Perform a query which includes aNamespace, and where it's selections consist of a single instance of the extension field (delegated to B
  4. Notice that the response contains aNamespace: null, when instead it should contain aNamespace: { extensionField: <some value> }

Expected behavior

If there are selections on a non-scalar field in the query, then it should be possible to resolve those selections, even if none of the selections happen to be resolved on the same subschema that resolved the field.

Environment:

  • OS:
  • @graphql-tools/stitch: https://github.com/ardatan/graphql-tools/commit/c30c1c778fc9b0b835544364ce278a9609b2ee0b (9.0.3)
  • NodeJS: 16.14.2

Additional context

This can be worked around by adding a field to the selection set that is resolved by the same subschema that resolves the field. For example in the reproduction, the test executes stitched query with dummy field passes, but the test executes stitched query without dummy field fails.

An automated way of doing this (also transparent to the user of the API) is to use the following transform, which adds __typename to every selection set in the query:

{
  transformRequest: (originalRequest): ExecutionRequest => ({
    ...originalRequest,
    document: visit(originalRequest.document, {
      SelectionSet(selectionSet) {
        return {
          ...selectionSet,
          selections: selectionSet.selections.some(
            (selection) =>
              selection.kind === Kind.FIELD &&
              selection.name.value === '__typename',
          )
            ? selectionSet.selections
            : [
                ...selectionSet.selections,
                {
                  kind: Kind.FIELD,
                  name: {
                    kind: Kind.NAME,
                    value: '__typename',
                  },
                },
              ],
        };
      },
    }),
  }),
}

See a reproduction here in the form of a failing test: https://github.com/ardatan/graphql-tools/pull/5741

hedgepigdaniel avatar Dec 01 '23 10:12 hedgepigdaniel