Skip to content

Support @SchemaMapping and @BatchMapping for schema interface types #871

Closed
@frenchtoast747

Description

@frenchtoast747

Instead of requesting to re-open #294, I'm creating a new issue.

First off, thank you for this library! It has come a long way since I first started looking into Java + GraphQL over 2 years ago. There have been a lot of great QoL improvements everywhere! Thank you!

Between issues #294 and #236 the suggestion was to create a base batching function and then write a new batching function for each concrete type citing that DGS and NestJS don't support the functionality requested. I'd like to revisit this.

Here's my use-case:

interface Activity {
    id: ID!
    completedBy: [User!]!  # requires a dataloader
    labels: [Lable!]!  # requires a dataloader
    # other fields here...
}

type FooActivity implements Activity {
    # repeat Activity fields here...
    foo: Foo!
    # other fields related to Foo...
}

type BarActivity implements Activity {
    # repeat Activity fields here...
    bar: Bar!
    # other fields related to Bar...
}

Query {
    activities(...): [Activity!]!
}

I should be able to write a single test that executes activities() testing all of the Activity fields which should always pass no matter whether I have one or ten concrete implementations of Activity (though, per the GraphQL spec, I must have at least one).

Right now, I only have a handful of Activity types, but I plan to add more in the future. As I do, with the current state of the library, I will have to just remember to make sure to implement dataloaders for each new concrete type or risk a 500 error on a frontend query that is fetching one of the .users or .labels attributes.

I would like to point out that Python's Graphene library supports setting resolvers on an Interface type and therefore dataloaders so that I don't have to implement the same resolvers on all concrete types (Sadly, this isn't clearly documented as an example, otherwise I'd post a link. I've used it in production code and it just works). That being said, I have to usually supply a TypeResolver to help it out a bit.

Here is what I expect to work:

interface Activity { 
    // ... fields here
}

public class FooActivity implements Activity {}
public class BarActivity implements Activity {}

@BatchMapping
Mono<Map<Activity, List<User>>> completedBy(List<Activity> activities) {
    // use repositories to fetch data
}

However, here is my workaround:

// just a regular function on the controller
Mono<Map<Activity, List<User>>> activityCompletedBy(List<? extends Activity> activities) {
    // use repositories to fetch data
}

// due to type erasure, I have to name each method explicitly 
// and therefore must specify the GraphQL field name explicitly
@BatchMapping(field = "completedBy")
Mono<Map<Activity, List<User>>> fooActivityCompletedBy(List<FooActivity> activities) {
    return activityCompletedBy(activities);
}

@BatchMapping(field = "completedBy")
Mono<Map<Activity, List<User>>> barActivityCompletedBy(List<BarActivity> activities) {
    return activityCompletedBy(activities);
}

// same for the `labels` field, just swapping fields.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions