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

F# option type does not work for Node lookup resolver (even when it works for fields)

Open cmeeren opened this issue 1 year ago • 5 comments

Product

Hot Chocolate

Version

14.0.0-p.79

Link to minimal reproduction

See zip below

Steps to reproduce

Repro: Repro.zip

The entire code for quick reference:

namespace HotChocolateRepro

open HotChocolate.Types.Relay
open Microsoft.AspNetCore.Builder
open Microsoft.Extensions.DependencyInjection
open Microsoft.Extensions.Hosting

[<Node>]
type MyType(id: string) =

    static member GetMyType(id: string) = Some (MyType(id))

    member _.Id() = id

    // This Option-wrapped field is just to show that Option-wrapping actually works for fields
    member _.Nullable: string option = None


type Query() =

    member _.My() = MyType("1")


module Program =

    [<EntryPoint>]
    let main args =
        let builder = WebApplication.CreateBuilder(args)

        builder.Services
            .AddGraphQLServer()
            .AddQueryType<Query>()
            .AddFSharpTypeConverters()
            .AddGlobalObjectIdentification()
        |> ignore

        let app = builder.Build()
        app.MapGraphQL() |> ignore
        app.Run()
        0

Here is the query to reproduce:

query {
  node(id: "TXlUeXBlCmQx") {
    id
    ... on MyType {
      nullable
    }
  }
}

What is expected?

{
  "data": {
    "node": {
      "id": "TXlUeXBlCmQx",
      "nullable": null
    }
  }
}

What is actually happening?

{
  "errors": [
    {
      "message": "Could not resolve the actual object type from `Microsoft.FSharp.Core.FSharpOption`1[[HotChocolateRepro.MyType, HotChocolateRepro, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]` for the abstract type `node`.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "node"
      ]
    }
  ],
  "data": {
    "node": null
  }
}

Relevant log output

No response

Additional context

It works fine when the Node lookup resolver does not return an option-wrapped value:

static member GetMyType(id: string) = MyType(id)

cmeeren avatar Mar 26 '24 20:03 cmeeren

I have tried adding a formatter or adding/prepending a middleware in various TypeInterceptor hooks, but it does not seem to work.

I'd welcome any insights as to how I can use either a TypeInterceptor or something else to accomplish this, or confirmation that this is in fact impossible and requires changes to HC internals.

cmeeren avatar Sep 23 '24 08:09 cmeeren

This works as long as it's done before completion. But you actually do not want to solve that with a middleware as this would slow everything down that is optional. Optional in this case represents a primitive an needs to be understood by the engine.

michaelstaib avatar Sep 24 '24 19:09 michaelstaib

As mentioned I also tried a formatter, but no luck.

Am I understanding you correctly that this needs changes in HC internals, and there is nothing I can do in the library right now?

cmeeren avatar Sep 24 '24 21:09 cmeeren

I think I know what this is .... I will have a look. The issue with node resolvers is that they kind of do not exist and are just folded into the parent. This means you cannot get a formatter into the field.

michaelstaib avatar Sep 25 '24 21:09 michaelstaib

@michaelstaib, any chance this can be fixed in v16? (Preferably as well as #7023 and other issues blocking the FSharp.HotChocolate v1.0 milestone.)

cmeeren avatar Oct 21 '25 17:10 cmeeren