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

Can't override an existing field with an object type

Open mbrowne opened this issue 3 years ago • 5 comments

Issue workflow progress

Progress of the issue based on the Contributor Workflow

  • [x] 1. The issue provides a reproduction available on CodeSandbox

  • [ ] 2. A failing test has been provided

  • [ ] 3. A local solution has been provided

  • [ ] 4. A pull request is pending review


Describe the bug

additionalTypeDefs can be used to change the type of a field in a GraphQL source schema (together with the filter transform to remove the original field), but only if it's a primitive field. If you try to set it to an object type, it fails with this error:

No type was found for field node { kind: "Field", alias: undefined, name: { kind: "Name", value: "label" }, arguments: [], directives: [], selectionSet: undefined }.

To Reproduce Steps to reproduce the behavior:

Open this CodeSandbox: https://codesandbox.io/s/modest-lehmann-2uzylv

Run this query:

    query Author {
      author(id: 1) {
        id
        name {
          label
          value
        }
      }
    }

Expected behavior The name field should be overridden with the new type, TextDisplayField.

Environment:

  • OS: Mac OS 12
  • "@graphql-mesh/cli": "0.66.1"
  • NodeJS: 16.12.0

mbrowne avatar Mar 31 '22 20:03 mbrowne

Hi @mbrowne , thanks for the codesandbox.

I had a quick look. You're right, filtering out a field and then re-introducing it with additional type definitions seems to cause a clash.

I have been able to get your example working by using the filter-schema transform in bare mode. The config needs to change from this:

    transforms:
      - filterSchema:
          filters:
            - Author.!name

To this:

    transforms:
      - filterSchema:
          mode: bare
          filters:
            - Author.!name

bare transforms have their own complexities, so there are reasons why transforms work in wrap mode by default. However, apart from the fact that I am a huge supporter of bare mode; I believe that using bare especially to filter out things from a schema is very safe. If you want to learn more about bare and wrap modes, have a look at the dedicated paragraph on the documentation.

Having said that, this will allow you to hopefully get going, but I believe the team will still want to look into the origin of the clash and whether there is feasibility for improving there.

santino avatar Apr 01 '22 10:04 santino

Thanks for the quick reply. This works for a local schema, but as soon as I change it to use endpoint and point it to my Graphql server, it no longer queries the name field on the source, so the name data is missing (even though the actual query I'm running in GraphiQL is the same). And actually this happens even if I add a totally unrelated graphql source using endpoint rather than schema.

But it's fine; I can wait until the underlying issue is resolved - thanks!

mbrowne avatar Apr 01 '22 14:04 mbrowne

The underlying issue is surely going to be complex and it won't necessarily be feasible to solve.

If you filter out the name field from the schema, then it makes sense that this is not queried anymore against the original schema. This requirement wasn't explained.

If your goal is to query for the original name field in the original schema, you should probably define this requirement as a selectionSet that will enrich the request sent against the original schema. If you check again on the dedicated documentation guide, you will see a few examples with selectionSet that might be able to guide you on how to explicitly include extra fields in the document requested to your underlying data source.

Your codesandbox can no longer be used for this, if you need to point to an actual data source in order to replicate your real needs. Because of that I cannot help you further, but hopefully the selectionSet is a good next step to look at.

santino avatar Apr 01 '22 16:04 santino

Could you add another source(it can be a dummy one) and try again?

ardatan avatar Apr 01 '22 16:04 ardatan

Thanks for the tips. The interesting thing is that it worked with just the local source, but as soon as you add even one external graphql source, it no longer fetches the name field from the AuthorService. e.g. if you add this to the config in the sandbox link above:

  - name: SpaceX
    handler:
      graphql:
        endpoint: https://api.spacex.land/graphql/

And when both are external sources, even this no longer works:

      - filterSchema:
          mode: bare
          filters:
            - Author.!name

(I get Error: Field "Author.name" already exists in the schema. It cannot also be defined in this type extension.)

I could create a demo branch in github if that's helpful.

I'm not sure if I would be able to use requiredSelectionSet since I already have a custom resolver, but I might be able to run a custom query as shown in the docs, e.g. context.AuthorService.Query.author(). But I think I would need to do that at the query level...I was able to get it to pick up a customer resolver for Query.author by using filterSchema to filter that out too (otherwise it doesn't pick it up), but then context.AuthorService doesn't exist in my resolver. I could just create a new query with a different name instead...I will keep experimenting with it.

mbrowne avatar Apr 01 '22 17:04 mbrowne