Batch nested relationship loads
A query loads a parent object and a list of related records, whose fields require further database access.
GraphQL · Const
The N+1 problem occurs when resolving one list triggers an additional backend request for each item, producing one initial request plus N follow-up requests. GraphQL performance guidance recommends batching and caching related loads, or translating selection sets into optimized data-source queries.
A naive resolver can issue one backend call for a parent relationship and then another call for each item in the resulting list. Batching collects these related loads over a short interval and dispatches them as one database or service request, reducing repeated backend work. Some GraphQL implementations can instead translate selection sets into optimized data-source queries.
const loaders = {
starshipsByOwner: new DataLoader((humanIds) => fetchStarshipsByHumanIds(humanIds))
};
const resolvers = {
Human: {
starships: (human, _args, context) =>
context.loaders.starshipsByOwner.load(human.id)
}
};The resolver requests each human's starships through a shared loader, allowing those requests to be collected and sent to the backend together.
A query loads a parent object and a list of related records, whose fields require further database access.
Several sibling resolvers request records from the same backend during one operation.
A server must reduce repeated database or microservice calls caused by list field resolution.
The N+1 problem occurs when one data-source request obtains a list, followed by one request per list item to resolve related fields. For example, loading friends once and then loading each friend's starships separately creates the pattern.
Follow-up: How would you mitigate it? Collect the individual loads over a short interval and dispatch them as one backend request, commonly with a batching utility such as DataLoader.
Use batching so multiple related resolver requests are combined into one request to the database or microservice. Caching requests can also mitigate the problem during field execution.
Follow-up: What alternative can an implementation provide? It may translate an operation's selection sets into optimized queries for the underlying data source.
The issue arises from GraphQL's field-level resolver model: each field can have a focused resolver, but independently executing those resolvers may repeatedly access the backend. Batching and caching requests helps control that access.
Follow-up: Is batching alone sufficient for all expensive operations? No; pagination and limits on operation depth and breadth are also recommended to prevent excessive server load.
DataLoaderfield resolversselection setsbatchingcachingpaginationoperation depthoperation breadth