Skip to content

Resolvers for sub fields #8263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
TylerSiegrist opened this issue Apr 25, 2025 · 3 comments
Closed

Resolvers for sub fields #8263

TylerSiegrist opened this issue Apr 25, 2025 · 3 comments

Comments

@TylerSiegrist
Copy link
Contributor

Product

Hot Chocolate

Is your feature request related to a problem?

Resolvers are not run for properties of properties...

descriptor.Field(x => x.Foo.Bar).Resolve(ctx => "bar") // This is never run

I have a use case for it where I have several types implementing a shared interface. Depending on the implementation type, different downstream APIs should be called to fetch certain fields. I have created a minimal repository with a basic test to reproduce: https://github.com/TylerSiegrist/HotChocolate.Example.Resolver.Subfields

The solution you'd like

Resolvers are executed for properties of properties.

@michaelstaib
Copy link
Member

I do not follow what you want to achieve. Why dont you define this on the author type? Do you want different resolvers depending on the path the execution takes?

@TylerSiegrist
Copy link
Contributor Author

TylerSiegrist commented Apr 28, 2025

The latter is correct, which is particularly needed when we have types inheriting. In our scenario we have an Account interface with several implementations. There's a lot of available properties, so we have them organized into several objects such as "Location" which are properties on Account. For a FooAccount we may fetch most Location data from ServiceA but for a BarAccount we may fetch most data from ServiceB. Since Location is its own type, we have no way to define how to fetch individual properties of Location from the IObjectTypeDescriptor<FooAccount> level. If Location.addressType is fetched from a third API for both Account types (ServiceC) we have to define the resolver to fetch data from both (ServiceA/ServiceB) and ServiceC as we have to hydrate the entire Location object or inspect the selections within the resolver.

Being able to do descriptor.Field(x => x.Location.AddressType).Resolve(ctx => /* fetch from ServiceC */) is much simpler than having to do...

descriptor.Field(x => x.Location.AddressType).Resolve(ctx =>
{
    var location = new Location();
    if(/* check if addressType is selected */)
    {
        // Load data from ServiceC
        location.AddressType = serviceCData.AddressType;
    }
    if(/* check if any other fields are selected */)
    {
        // Load data from ServiceB
        location.Address1 = serviceBData.Address1;
        location.Address2 = serviceBData.Address2;
    }
    return location;
})
interface Account 
{
    name: String!
    sourceSystem: String!
    location : Location!
}

type Location
{
    addressType: String! // Fetched from ServiceC for all Accounts
    address1: String! // The rest of the fields are fetched from ServiceA or ServiceB
    address2: String!
    city: String!
    state: String!
    zip: String!
}

type FooAccount implements IAccount
{
    name: String!
    sourceSystem: String!
    location: Location! // Should be fetched from ServiceA+ServiceC
}

type BarAccount implements IAccount
{
    name: String!
    sourceSystem: String!
    location: Location! // Should be fetched from ServiceB+ServiceC
}

@michaelstaib
Copy link
Member

To solve this you have to use scoped state which is inherited by child resolverS. Then do a decision based on this. You could even store a delegate in the state that represents the changed execution flow and combine it in essence with a middleware. You could I think get very close to the API you suggest. As for the core I do not see it as I would from the code expect that we would pull the field up. You are defining in the type API fields for this type only. So this would break predictability of our API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants