AntoineB
AntoineB

Reputation: 4694

GraphQL + Java - How do I filter subfields?

I am using graphql-java with graphql-java-annotations in my Spring application (spring-graphql-common seemed very limited and looks like it's not maintained anymore), and I'm struggling with subfields manipulation.

I have two classes A and B that I want to access using GraphQL, here they are:

class A {
    @GraphQLField
    Integer id;
    @GraphQLField
    String name;
    @GraphQLField
    List<B> listOfB;
}

class B {
    @GraphQLField
    Integer id;
    @GraphQLField
    String code;
}

I can successfully query all the fields in A and also the fields in B using the following schema:

@Component
public class Schema
{
    public GraphQLObjectType aType;
    public GraphQLSchema aSchema;
    public GraphQLObjectType queryType;

    @Autowired
    public Schema(DataFetcher dataFetcher) throws IllegalAccessException, InstantiationException, NoSuchMethodException
    {
        aType = GraphQLAnnotations.object(A.class);

        queryType =
                GraphQLObjectType
                .newObject()
                    .name("QueryType")
                    .field(GraphQLFieldDefinition.newFieldDefinition()
                        .name("a")
                        .type(aType)
                        .argument(GraphQLArgument.newArgument()
                                .name("id")
                                .type(new GraphQLNonNull(Scalars.GraphQLInt))
                                .build())
                        .dataFetcher(dataFetcher)
                        .build())
                    .build();

        aSchema = GraphQLSchema.newSchema()
                .query(queryType)
                .build();
    }
}

I can successfully filter on a with the following request:

{
    a(id:5)
    {
        id,
        name,
        listOfB
        {
            code
        }
    }
}

But when I try to filter on listOfB, for example with a take to select only X records, I have the error Validation error of type UnknownArgument: Unknown argument take:

{
    a(id:5)
    {
        id,
        name,
        listOfB(take:3)
        {
            code
        }
    }
}

I understand what the error means, as I haven't declared any possible argument for the listOfB field on A, but I don't know how I could possibly do it using graphql-java-annotations.

I have added a specific DataFetcher for listOfB tho, using the following code in class A:

@GraphQLDataFetcher(BDataFetcher.class)
private List<B> listOfB;

and it is indeed getting called when I retrieve listOfB.

Do you have any idea on how I could add arguments to fields when using graphql-java-annotations? If not, is there any workaround?

Defining the schema without annotations and using the "classic" way of graphql-java is not an option as my schema is really huge :/

Thanks :)

Upvotes: 2

Views: 7033

Answers (2)

Guy Smorodinsky
Guy Smorodinsky

Reputation: 952

I highly recommend you to look again at this library, as it is now back to be maintained, and the readme is very clear.

As for you question: You should make you field listOfB like this:

@GraphQLField
@GraphQLDataFetcher(ListOfBFetcher.class)
List<B> listOfB(int take){return null};

and in the dataFetcher you can get the take argument from the environment variable (via enviroment.getArgument("take"))

You can also make the method the dataFetcher itself by doing:

@GraphQLField
List<B> listOfB(int take, DataFetchingEnvironment environment){
    //return the list of B
  };

Upvotes: 1

AntoineB
AntoineB

Reputation: 4694

It is not the solution I ended up using because it doesn't fit my design, but here's what you can do:

public class MyClass
{
    @GraphQLField
    private Integer id;
    private List<String> items;

    @GraphQLField
    public List<String> items(DataFetchingEnvironment environment, @GraphQLName("take") Integer take, @GraphQLName("after") Integer after)
    {
        List<String> items = ((MyClass) environment.getSource()).getItems();

        Integer fromIndex = 0;
        Integer toIndex = items.size();
        if (after != null)
            fromIndex = after + 1;
        if (take != null)
            toIndex = fromIndex + take;

        return items.subList(fromIndex, toIndex);
    }
}

It sucks because you're not using a DataFetcher so your code will be copy/pasted everywhere, but it still works.

But I found a way to use a DataFetcher, that said it's weird and I guess it's not a proper use of the library, but who knows:

public class MyClass
{
    @GraphQLField
    private Integer id;
    private List<String> items;

    @GraphQLField
    @GraphQLDataFetcher(MyClassDataFetcher.class)
    public List<String> items(DataFetchingEnvironment environment, @GraphQLName("take") Integer take, @GraphQLName("after") Integer after)
    {
        return null;
    }

}

This will make the arguments take and after is the DataFetchingEnvironment in your MyClassDataFetcher.get() method, so you have an empty method that is not getting called but the arguments are still passed to your DataFetcher.

Again, it's not ideal because I doubt it's how it's meant to be used, but it's the only way of using a DataFetcher with arguments that I'm aware of.

Now, I didn't use any of those two solutions and I ended up dropping completely graphql-annotations and developed my own solution with custom annotations that are allowing me to automatically build the schema (won't be released publicly, sorry).

I wouldn't recommend using graphql-annotations until they have proper documentation for all the use cases.

Upvotes: 1

Related Questions