MichaelB
MichaelB

Reputation: 1132

SpringBoot GraphQL field name mismatch

It seems that the names defined in the .graphqls file MUST match the field names in the POJO. Is there a way to maybe annotate the field so that they don't have to?

For example, I have something like this in the graphqls file

type Person {
     personId: ID!
     name: String!
}

Then in my Entity POJO I have like

@Id
@Column(name="PERSON_ID")
@JsonProperty("person_id")
private int personId;

@Column(name="NAME")
@JsonProperty("name")
private String name;

So, the intention is for the field name to be personId and the database to store it as a column called PERSON_ID and for it to get serialized as JSON and GraphQL as person_id

But graphql talks in the language of the schema. So it serializes it as personId which matches the schema field but is not the intention. I could change the schema to be person_id but then I need to change the field too... This isn't the end of the world but it's quite "un-javalike" to have fields named that way.

I am using this library:

compile group: 'com.graphql-java', name: 'graphql-spring-boot-starter', version: '5.0.2'

I have also seen the @GraphQLName annotation in the annotations library but I must be missing something because it doesn't do what I am expecting or maybe I am using it wrong.

Is there some way to get around this or should I just change the names?

Upvotes: 3

Views: 1954

Answers (2)

Joan Segura
Joan Segura

Reputation: 13

You can always define your WiringFactory and provide a default DataFetcher. In this example, the new DataFetcher uses reflection to find the method with JsonProperty annotation that matches the field name. If none, it uses the built-in PropertyDataFetcher.

@Bean
public RuntimeWiringConfigurer runtimeWiringConfigurer() {
    return wiringBuilder -> wiringBuilder
            .wiringFactory(new WiringFactory() {
                @Override
                public DataFetcher<Object> getDefaultDataFetcher(FieldWiringEnvironment fieldWiringEnvironment) {
                    return dataFetchingEnvironment -> Arrays.stream(dataFetchingEnvironment.getSource().getClass().getDeclaredMethods()).filter(
                            method -> (method.getName().contains("get") && method.getAnnotation(JsonProperty.class).value().equals(dataFetchingEnvironment.getField().getName()))
                    ).map(
                            method -> {
                                try {
                                    return method.invoke(dataFetchingEnvironment.getSource());
                                } catch (IllegalAccessException | InvocationTargetException e) {
                                    throw new UncheckedExecutionException(e);
                                }
                            }
                    ).findAny().orElseGet(
                            () -> new PropertyDataFetcher<>(dataFetchingEnvironment.getField().getName()).get(dataFetchingEnvironment)
                    );
                }
            })
           .scalar(buildObjectScalar());
}

Upvotes: 1

felipe_gdr
felipe_gdr

Reputation: 1088

GraphQL Java uses PropertyDataFetcher by default to resolve field values (see data fetching section in the docs). This data fetcher works out-of-the box when the data object returned by a top level field data fetcher contains child fields that match the data object property names.

However, you can define your own data fetcher for any field and use whatever rule you need.

So, if you want a schema that looks like this

type Person {
     person_id: ID!
     name: String!
}

and your entity like this:

class Person {
    private int personId;

    private String name;

    // getters and setters
}

You can write a simple custom data fetcher for the field personId

private RuntimeWiring buildWiring() {
    return RuntimeWiring.newRuntimeWiring()
            // query root data fetchers wiring
            .type(newTypeWiring("Person")
                    .dataFetcher("person_id", environment -> {
                        Person person = environment.getSource();
                        return person.getPersonId();
                    })
            )
            // maybe other wirings
            .build();
}

Upvotes: 2

Related Questions