Spring for GraphQL: addressing over- and under- fetching by changing schema structure?

Spring for GraphQL: addressing over- and under- fetching by changing schema structure?


0

I’m using Spring for GraphQL and having trouble understanding how to make my graphql resolvers avoid over- and under- fetching without modifying my schema to reflect how data is grouped in my database.

I’ve read about the N+1 problem but I think this is slightly different. Consider the following graphql schema:

type Query {
    employee(name: String, companyName: String): Employee
}

type Employee {
    name: String
    companyName: String
    age: Int # source: Person table
    address: String # source: Person table
    jobTitle: String # source: Employee table
    startDate: String # source: Employee table
}

Is there a way to set up resolvers to always fetch age and address together? And to fetch jobTitle and startDate together?

I could modify the schema to group those fields, but then it is tightly coupled to my database:

type Employee {
    name: String
    companyName: String
    personalDetails : PersonalDetails # source: Person table
    jobDetails: JobDetails # source: Employee table
}

type PersonalDetails {
    age: Int
    address: String
}

type JobDetails {
    jobTitle: String
    startDate: String
}

As an example, the following code uses four resolvers to populate Employee, but I’d like to just use two:

@Controller
public class GraphqlController {
    record Employee(String name, String companyName, Integer age, String address, String jobTitle, String startDate) {
    }

    @QueryMapping
    public Employee employee(@Argument String name, @Argument String companyName) {
        return new Employee(name, companyName, null, null, null, null);
    }

    @SchemaMapping
    public Integer age(Employee employee) {
        return 35;
    }

    @SchemaMapping
    public String address(Employee employee) {
        return "US";
    }

    @SchemaMapping
    public String jobTitle(Employee employee) {
        return "Software";
    }

    @SchemaMapping
    public String startDate(Employee employee) {
        return "Monday";
    }
}

1 Answer
1


0

Right now your example is a bit artificial in that your field resolvers are returning static values. In a real system your resolvers are going to be accessing a database.

A GraphQL type will typically map fairly closely to a table or document collection. It doesn’t have to but the main point is that a db query can return multiple columns at a time.

Let’s say your Employee type maps to one or two tables in a relational database. When a resolver is resolving one or more Employee records it can do:

select * from Employee where id=123;

If your GraphQL field names are the same as the underlying database column names then your Employee resolver can just return the record as is. GraphQL will take care of removing any fields that were not in the original request. If a particular field name does not match the underlying column name then you can write a simple field resolver that just renames the column. Remember: the parent object that is passed to your field resolver will be the entire db record including all the original columns.

Where things get more interesting is when your GraphQL fields map to other tables. That’s when the N+1 problem comes up but where you also get the benefit of GraphQL only running resolvers for fields that have been requested in the query. If you have a complex resolver but the client isn’t requesting that data, that resolver will not run.

Some time ago I published a post which goes into a lot of this. Although the post uses JS instead of Java, the rdb-GraphQL mapping concepts still apply.



Leave a Reply

Your email address will not be published. Required fields are marked *