ChilliCream / graphql-platform

Welcome to the home of the Hot Chocolate GraphQL server for .NET, the Strawberry Shake GraphQL client for .NET and Banana Cake Pop the awesome Monaco based GraphQL IDE.
https://chillicream.com
MIT License
5.27k stars 748 forks source link

FieldResult<TResult> fails field resolver #7598

Open DovydasNavickas opened 1 month ago

DovydasNavickas commented 1 month ago

Product

Hot Chocolate

Version

14.0.0-rc.3.4

Link to minimal reproduction

https://github.com/MartynasZilinskas/hotchocolate-fieldresult-bug-repro/tree/main

Steps to reproduce

  1. Run the repro project
  2. Run query successfully, because createdBy resolver doesn't use FieldResult<TResult> and returns TResult directly:
    query {
    book {
    id
    title
    createdBy {
      id
      name
    }
    }
    }
  3. Run failing query, because modifiedBy resolver returns FieldResult<TResult>:
    query {
    book {
    id
    title
    createdBy {
      id
      name
    }
    modifiedBy {
      id
      name
    }
    }
    }

What is expected?

I expect FieldResult<TResult> to successfully return both in the root field resolver and in a non-root field resolver.

What is actually happening?

Query:

query {
  book {
    id
    title
    createdBy {
      id
      name
    }
    modifiedBy {
      id
      name
    }
  }
}

Response:

{
  "errors": [
    {
      "message": "Cannot return null for non-nullable field.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "book"
      ],
      "extensions": {
        "code": "HC0018"
      }
    },
    {
      "message": "Cannot return null for non-nullable field.",
      "locations": [
        {
          "line": 9,
          "column": 5
        }
      ],
      "path": [
        "book",
        "modifiedBy"
      ],
      "extensions": {
        "code": "HC0018"
      }
    },
    {
      "message": "The resolver parent type of field `Author.id` is `HotChocolate.FieldResult`1[Graphql.ApiService.Contracts.Author]` but the resolver requested the type `Graphql.ApiService.Contracts.Author`. The resolver was unable to cast the parent type to the requested type.",
      "path": [
        "book",
        "modifiedBy",
        "id"
      ],
      "extensions": {
        "fieldCoordinate": "Author.id",
        "code": "HC0053"
      }
    }
  ],
  "data": null
}

Relevant log output

No additional logs are output with default settings, only the failing response output.

Additional context

No response

DovydasNavickas commented 1 month ago

As discussed with @michaelstaib in Slack, there were 2 problems:

  1. Missing .AddQueryConventions() while configuring the GraphQL server.
  2. Missing [Error<ModifiedByUserNotFoundError>] attribute on the resolver returning FieldResult<TResult>

Commit fixing both is here.

The only difference is the resulting query is now a union: And need to be queried as such:

query {
  book {
    id
    title
    createdBy {
      id
      name
    }
    modifiedBy {
      ... on Author { # <----------- query on successful result
        id
        name
      }
      ...on ModifiedByUserNotFoundError { # <----------- query on specific error
        message
      }
      ...on Error { # <----------- query on any error
        message
      }
    }
  }
}

We also agreed that the error should be a schema error and not the one returned right now.