graphql-nexus / nexus

Code-First, Type-Safe, GraphQL Schema Construction
https://nexusjs.org
MIT License
3.4k stars 274 forks source link

N+1 issues when getting nested types #869

Closed mattoni closed 3 years ago

mattoni commented 3 years ago

Let's say I have a list of certain stocks, with a query getList query that gets all stocks on the list with their latest price.

query getListWithPrices($listId: String!) {
  getList(listId: $listId) { 
    id
    name
    stocks {
       id
       name
       latestPrice
    }
  }
}

Where latestPrice is a query that grabs pricing from the historical pricing table, and orders by date desc grabbing the first result.

Structuring this with Nexus + Prisma is leading to an N+1 issue, and I was hoping to find some help. Here's how things are so far:

  1. The list resolver grabs the correct list by ID (1 Query)
  2. The list has 100 stocks (1 Query, all stocks who are a member of the list),
  3. For each stock, query the priceHistory table, order by desc date, and grab the first one (100 Queries, as the field exists on the stock resolver)

Is there a way to do this in a more efficient way without needing to create a completely separated query for handling bulk price lookups like this?

Thanks in advance.

dmitrykozyrenko commented 3 years ago

Same issue.

Nayni commented 3 years ago

This isn't really a nexus problem but rather a challenge when creating nested graph-like structures in GraphQL in general.

There are a few ways to deal with this but the most common approach to handle this is to use the dataloader pattern. This pattern was made popular by Facebooks dataloader library for NodeJS.

The gist of the library is rather simple. While resolving your query it will collect all the Stock.id 's that need a latestPrice it will then give you all those id's in a batch so you can resolve them. Generally in SQL-like queries this would result in something like: SELECT * FROM priceHistory WHERE stock_id IN (...). This makes is so that when you resolve 100 stocks in the list you only issue 1 query to the priceHistory table.

If you want to know more check out the dataloader library on GitHub :)

mattoni commented 3 years ago

Thanks for the detailed response! I found dataloader not long after, and forgot to comment on this issue. I think the nexus docs could add some sections describing some of this stuff, as I've had to resort to stack overflow and issues on github to find solutions to problems I feel are pretty common and should be addressed.

Anyway, appreciate the response!