4
votes

Suppose you have a GraphQL layer, written on node.js using graphql-js, that communicates with a SQL database. Suppose you have the following simple types and fields:

  • Store
    A single brick-and-mortar location for a chain of grocery stores.
    Fields:

    • id: GraphQLID
    • region: StoreRegion
    • employees: GraphQLList(Employee)
  • StoreRegion
    A GraphQLEnumType containing the list of regions into which the chain divides its stores.
    Values:

    • NORTHEAST
    • MIDATLANTIC
    • SOUTHEAST
    • ...
  • Employee
    Represents a single employee working at a store.
    Fields:

    • id: GraphQLID
    • name: GraphQLString
    • salary: GraphQLFloat

Suppose the API exposes a store query that accepts a Region and returns a list of Store objects. Now suppose the client sends this query:

{
    store(region: NORTHEAST) {
        employees {
            name
            salary
        }
    }
}

Hopefully this is all pretty straightforward so far.

So here's my question, and I hope (expect, really) that it's something that has a common solution and I'm just having trouble finding it because my Google-Fu is weak today: is there a good way that can I write the resolvers for these types such that I can wrap up all the requested fields for all the employees from all the returned stores into a single SQL query statement, resulting in one round-trip to the database of the form:

SELECT name,salary FROM employees WHERE id IN (1111, 1133, 2177, ...)

rather than making one request per employee or even one request per store?

This is really a concrete instance of a more general question: is there a good way to combine resolvers to avoid making multiple requests in cases where they could be easily combined?

I'm asking this question in terms of graphql-js because that's what I'm hoping to work with, and since I figure that would allow for more specific answers, but if there's a more implementation-agnostic answer, that would be cool too.

1
While definitely possible, this is essentially optimizing for one particular query to your API. Unless you already know for sure that you'll run into performance issues as soon as you deploy, it's probable most pragmatic to use simple resolvers and optimize problem queries once they become a problem :-) Otherwise you'll likely end up with many single-use optimizations that make changes far more difficult. - Mike Marcacci

1 Answers

2
votes

So, basically you are wondering how you can combine multiple resolvers into fewer database queries. This is trying to solve what they call the N+1 query problem. Here’s at least two ways you can solve this.

  1. DataLoader: This is a more general solution and it's created by Facebook. You could use this to batch multiple queries that query a single item of a single type into a single query that queries multiple items of a single type. In your example you would batch all employees into a single DB query and you would still have a separate query for getting the store. Here's a video by Ben Awad that explains DataLoader

  2. JoinMonster: Specifically for SQL. Will do JOINs to make one SQL query per graphql query. Here's a video by Ben Awad explaining JoinMonster