Dev Overflow Logo

Dev Overflow

Global search

Search across questions, answers, users and tags.

Loading...
save

Why is my GraphQL API making hundreds of database queries per request?

clock icon

asked 2 months ago

message icon

1

eye icon

819

Fetching 50 posts with their authors produces 51 SQL queries. Each post resolver looks up its own author:

1const resolvers = {
2 Post: {
3 author: (post) => db.user.findById(post.authorId),
4 },
5}
1const resolvers = {
2 Post: {
3 author: (post) => db.user.findById(post.authorId),
4 },
5}

Is there a standard fix, or do I need to hand-write joins per query shape?

1 Answer

This is the classic N+1, and the standard fix is a DataLoader — it batches all the lookups made within one tick into a single query:

1const userLoader = new DataLoader(async (ids) => {
2 const users = await db.user.findMany({ where: { id: { in: ids } } })
3 const byId = new Map(users.map((u) => [u.id, u]))
4 return ids.map((id) => byId.get(id))
5})
6
7const resolvers = {
8 Post: { author: (post) => userLoader.load(post.authorId) },
9}
1const userLoader = new DataLoader(async (ids) => {
2 const users = await db.user.findMany({ where: { id: { in: ids } } })
3 const byId = new Map(users.map((u) => [u.id, u]))
4 return ids.map((id) => byId.get(id))
5})
6
7const resolvers = {
8 Post: { author: (post) => userLoader.load(post.authorId) },
9}

51 queries becomes 2. Two rules that people trip over:

  1. Create the loader per request, not per process, or users will see each other's cached data.
  2. The batch function must return results in the same order as the input ids, including null for misses.

1

of 1

Write your answer here

Introduce the problem and expand on what you've put in the title.

Top Questions