What is the GraphQL n+1 problem and why does it matter?
The GraphQL n+1 problem occurs when a query requesting nested data (e.g., a list of musicians and each musician's albums) results in one query for the list and then n additional queries—one for each nested item. This can lead to performance bottlenecks, increased latency, and higher infrastructure costs as the application scales. For example, fetching 100 musicians and their albums could result in 101 database calls instead of just a few. Note: This is a common challenge in GraphQL APIs and requires careful backend design to avoid scalability issues. Source.
How can the GraphQL n+1 problem be solved?
The n+1 problem in GraphQL can be addressed using data loaders and batching techniques. Data loaders batch similar requests into a single query, reducing the number of database or API calls. For example, instead of making n separate requests for each musician's albums, a data loader can fetch all albums for all musicians in a single call. Batching libraries are available for various languages (e.g., dataloader for JavaScript, Shopify's graphql-batch for Ruby). Note: Implementation details depend on your server language and framework. Source.
Does Hygraph help solve the GraphQL n+1 problem?
Hygraph provides a ready-to-use GraphQL API that abstracts away much of the backend complexity, including performance optimizations relevant to the n+1 problem. Its high-performance endpoints are optimized for low latency and high read-throughput, and caching strategies further reduce redundant queries. However, for highly complex nested queries, developers should still be aware of query design and backend integration patterns. Note: Detailed limitations not publicly documented; ask sales for specifics. Source.
What performance optimizations does Hygraph offer for GraphQL APIs?
Hygraph offers high-performance endpoints with low latency and high read-throughput, a read-only cache endpoint with 3-5x latency improvement, and active measurement of GraphQL API performance. These optimizations help deliver content efficiently and reliably. For more details, see the blog post and GraphQL Report 2024. Note: For extremely high-volume or custom backend scenarios, additional tuning may be required.
Features & Capabilities
What APIs does Hygraph provide?
Hygraph provides several APIs: a GraphQL Content API for querying and manipulating content, a Management API for handling project structure, an Asset Upload API for uploading files, and an MCP Server API for secure communication with AI assistants. For details, see the API Reference documentation. Note: Some advanced API features may require specific plans or configurations.
What integrations are available with Hygraph?
Hygraph integrates with a variety of platforms, including Digital Asset Management (DAM) systems (Aprimo, AWS S3, Bynder, Cloudinary, Imgix, Mux, Scaleflex Filerobot), hosting and deployment platforms (Netlify, Vercel), Product Information Management (Akeneo), commerce solutions (BigCommerce), and translation/localization tools (EasyTranslate). For a full list, visit the Hygraph Marketplace. Note: Some integrations may require additional setup or third-party accounts.
What technical documentation is available for Hygraph?
Hygraph offers extensive technical documentation, including API references, schema guides, integration tutorials, and AI feature documentation. Resources cover topics such as permissions, caching, webhooks, and onboarding. Access all documentation at hygraph.com/docs. Note: Some legacy features are documented separately for Hygraph Classic.
Use Cases & Business Impact
What types of companies and roles benefit most from Hygraph?
Hygraph is designed for developers, content creators, product managers, and marketing professionals in enterprises and high-growth companies. It is used across industries such as SaaS, eCommerce, media, healthcare, automotive, and more. Its flexibility and scalability make it suitable for teams modernizing their content management systems. Note: For highly specialized or legacy environments, additional integration work may be required.
What business impact can customers expect from using Hygraph?
Customers have reported faster time-to-market (e.g., Komax achieved 3x faster launches), improved customer engagement (Samsung saw a 15% increase), and cost reductions by replacing traditional CMS solutions. Other outcomes include enhanced content consistency and scalability. For more examples, see Hygraph case studies. Note: Results may vary depending on implementation and organizational readiness.
How long does it take to implement Hygraph?
Implementation timelines vary by project complexity. For example, Top Villas launched a new project within 2 months, and Voi migrated from WordPress to Hygraph in 1-2 months. Structured onboarding, starter projects, and community support help accelerate adoption. Note: Large-scale or highly customized migrations may require additional time. Source.
Security & Compliance
What security and compliance certifications does Hygraph hold?
Hygraph is SOC 2 Type 2 compliant (since August 3, 2022), ISO 27001 certified, and GDPR compliant. These certifications cover infrastructure, data protection, and privacy standards. For more details, visit the Secure Features page. Note: For industry-specific compliance needs, consult with Hygraph sales or support.
What security features are available in Hygraph?
Hygraph offers granular permissions, SSO integrations (OIDC/LDAP/SAML), audit logs, encryption in transit and at rest, regular backups, and secure API policies (custom origin policies, IP firewalls). All endpoints use SSL certificates. Note: Some features may require enterprise plans or additional configuration. Source.
Customer Proof & Success Stories
Who are some notable customers using Hygraph?
Notable customers include Samsung (15% improved engagement), Dr. Oetker, Komax (3x faster time-to-market), AutoWeb (20% increase in monetization), BioCentury, Voi, HolidayCheck, and Lindex Group. See case studies for details. Note: Outcomes are specific to each customer’s implementation.
What feedback have customers given about Hygraph's ease of use?
Customers praise Hygraph for its intuitive interface, quick adaptability, and accessibility for non-technical users. For example, Sigurður G. (CTO) noted the UI is intuitive, and Anastasija S. (Product Content Coordinator) highlighted instant front-end updates. Note: Some advanced features may require developer involvement. Source.
Pain Points & Problems Solved
What common pain points does Hygraph address?
Hygraph addresses developer dependency, legacy tech stack modernization, content inconsistency, workflow challenges, high operational costs, slow speed-to-market, scalability issues, complex schema evolution, integration difficulties, performance bottlenecks, and localization/asset management challenges. Note: For highly specialized requirements, additional customization may be necessary. Source.
Industries & Use Cases
Which industries are represented in Hygraph's case studies?
Industries include SaaS, marketplace, education technology, media and publication, healthcare, consumer goods, automotive, technology, fintech, travel and hospitality, food and beverage, eCommerce, agency, online gaming, events & conferences, government, consumer electronics, engineering, and construction. See case studies for examples. Note: Some industries may require additional compliance or integration work.
In this article, we will learn about the N+1 problem in GraphQL, its impact on performance, and effective strategies like data loaders and batching to overcome it.
Last updated by Aagam
on Jan 21, 2026
Originally written by Joanna
GraphQL is a query language and runtime to build APIs that reduces data results to only what the user requests. GraphQL uses schemas to define data inputs and responses from a single endpoint to a GraphQL runtime. The schemas allow clients to request specific information, so the responses will only include what the client needs.
In GraphQL the client specifies the data to return, but not how to fetch it from storage. Sometimes, a query could lead to unintentional, excessive backend requests. The n+1 problem is a typical example of when this can happen in GraphQL.
The n+1 problem is when multiple types of data are requested in one query, but where n requests are required instead of just one. This is typically encountered when data is nested, such as if you were requesting musicians and the names of their album titles. A list of musicians can be acquired in a single query, but to get their album titles requires at least one query per musician: one query to get n musicians, and n queries to get a list of albums for each musician. When n becomes sufficiently large, performance issues and failures can arise. This is a common situation when using GraphQL because the client has full flexibility in building the queries.
In this article, we'll take a closer look at the n+1 problem and what it looks like in practice. We'll also get an overview of efficient techniques to avoid the problem and improve performance.
GraphQL queries are made against a single endpoint. GraphQL servers like the Apollo Server allow the backend to define a GraphQL schema. The schema is a data model at the application layer that indicates how the client can query the database, just like a REST API contract.
When building a runtime in GraphQL, a unique resolver should be present for each discrete data type. The following example shows a sample Apollo Server setup, GraphQL schema and resolvers for different entity fields. The client can make calls against this runtime to get musician data, and also get album related data for the musician.
//Schema Definitions
const typeDefs = gql`
type Album {
id: ID!
title: String!
artistId: ID!
}
type Musician {
id: ID!
name: String!
albums: [Album]
}
type Query {
musicians: [Musician]
}
`;
//Resolver definitions
const resolvers ={
Query:{
musicians:()=>{
//Database/API call to get a list of musicians
return musicians;
},
},
Musician:{
albums:(musician)=>{
//Input is a single musician
//Database/API call to get a list of albums for this single musician
return albums
},
},
};
const server =newApolloServer({ typeDefs, resolvers });
server.listen(3000).then(({ url })=>{
console.log(`Starting new Apollo Server at ${url}`);
The client creates requests against the GraphQL server, tailored to include the information needed for display. In the server we have set up above, we have allowed clients the flexibility to request a list of musicians, and inside the musician resolver we are allowing the client to fetch associated albums per musician.
query {
musicians {
id,
name,
albums {
title
}
}
}
The client query above requests some musician related data and albums related data for each musician. We know how the schema and resolvers of this GraphQL server are designed, and the way the server will execute this query can lead to issues. In this case, the server will first fetch the list of musicians. Let’s say it finds n musicians in the database. For each musician found, the albums() resolver will be invoked to locate all the albums associated with that musician. This resolver will trigger a database call for each musician, which will be n calls. This means that in total, there will be n+1 database calls occurring. This is not very efficient and won’t scale after a point.
Consequences of the n+1 problem
The n+1 problem can lead to several client and server issues. The main problem is scaling, it might go unnoticed until a point, but as our application scales and n grows, the number of calls to the database or calls to the API that resolves our GraphQL fields will become unmanageable. Also, the latencies will start increasing, the product will behave inconsistently, pages without many nested calls will load quickly, while others that require nested data will be much slower. If you are using a cloud-based server like AWS or GCP to run the database, extra calls to the database will cost more in service fees and eventually all of it combined will lead to a very poor user experience. High latency reduces the ability to retain customers, and hence we should try to closely monitor it and fix the issues in our system as our application scales.
Since this is a common issue with GraphQL, there are well-established solutions for handling it. These include using Data Loaders or Batching.
Data loaders in GraphQL
Data loaders are a way to solve the n+1 problem. They can batch similar client GraphQL requests into a single query. Basically, consider them as an intermediate layer that converts multiple similar requests to a single batched request. For our case, instead of making n different requests for getting albums of each musician, data loaders will make one request send an array of musician ids and receive an array of album objects.
The implementation of data loaders depends on which version of GraphQL you are using. Some have built-in data loader functionality, like the java-dataloader for GraphQL Java.
GraphQL has a well maintained dataloader library that can be used in our GraphQL server. This utility mimics the original GraphQL calls with loaders passed to each resolver in the context value. The example below shows what the GraphQL query for musicians would look like with a data loader.
constDataLoader=require('dataloader');
// The dataloader takes in an array of musician ids and returns a promise that will return
// the album data for each musician.
const albumLoader =newDataLoader(musicianIds=>{
// DB call that accepts list of musicianIds.
returndatabaseCall(musicianIds)
// OR it can be an API call that accepts a list of musicianIds.
returnapiCall(musicianIds)
})
// Add the data loader to context
constcontext=async()=>{
const loaders ={
album:albumLoader(musicianIds),
// Create more loaders for other data that is nested in your schema
}
return loaders;
}
// Use this context in the Apollo Server definition to pass to each resolver when executed.
Once the data loader is set up and available in the context, the resolver should be updated to use that loader. The loader is only triggered once, for the list of musicians fetched, so the number of DB / API calls will be reduced to two.
Batching in GraphQL is an expansion of the data loader concept discussed in the previous section. Essentially, batching libraries provide ways to ensure that nested data is retrieved with fewer queries by defining how to group and load similar data. Note that the main GraphQL library also supports batch execution, which is a different concept about invoking multiple resolvers at once.
Promises are the key to how batching works in GraphQL. The request executes the appropriate resolver first, and tries to resolve all the requested fields. Where batch loaders are specified, the data is resolved as a promise. GraphQL Batch can then iterate through grouped data identifiers to fulfill the promises together by retrieving data with as few calls as the batch loader will allow.
There are many batch loaders to choose from, implemented in different languages. Choose the appropriate tool based on the language your client or server are implemented in. For those using Ruby, Shopify has produced an open source plugin to use. Ruby users could also use this popular open source plugin, while Javascript users can use an open source library.
Using the Shopify Ruby batching plugin, we can implement a batch loader on the server side.
First, define a custom loader that will be used to group database calls:
Next, apply the batching plugin to the GraphQL schema. It is advised that the plugin be defined after mutations so the batching can extend mutation fields and allow for cache clearing.
classMySchema<GraphQL::Schema
query MyQueryType
mutation MyMutationType
use GraphQL::Batch
end
Finally, use the batch loader class in the resolver with grouped identifiers to get a batch of nested data:
In this article, we have learned what is the n+1 problem in GraphQL and how the extra DB/API calls are not scalable and can lead to a poor user experience. GraphQL provides several options to streamline this issue like data loaders and batching which can be built directly into our server. If you want to use GraphQL for your application without having to build the entire server side from scratch, consider using Hygraph, a headless server that can host your data and provide you with a ready to use GraphQL API out of the box.
Blog Authors
Aagam Vadecha
Joanna Wallace
Share with others
Sign up for our newsletter!
Be the first to know about releases and industry news and insights.
In this article, we will learn about the N+1 problem in GraphQL, its impact on performance, and effective strategies like data loaders and batching to overcome it.
Last updated by Aagam
on Jan 21, 2026
Originally written by Joanna
GraphQL is a query language and runtime to build APIs that reduces data results to only what the user requests. GraphQL uses schemas to define data inputs and responses from a single endpoint to a GraphQL runtime. The schemas allow clients to request specific information, so the responses will only include what the client needs.
In GraphQL the client specifies the data to return, but not how to fetch it from storage. Sometimes, a query could lead to unintentional, excessive backend requests. The n+1 problem is a typical example of when this can happen in GraphQL.
The n+1 problem is when multiple types of data are requested in one query, but where n requests are required instead of just one. This is typically encountered when data is nested, such as if you were requesting musicians and the names of their album titles. A list of musicians can be acquired in a single query, but to get their album titles requires at least one query per musician: one query to get n musicians, and n queries to get a list of albums for each musician. When n becomes sufficiently large, performance issues and failures can arise. This is a common situation when using GraphQL because the client has full flexibility in building the queries.
In this article, we'll take a closer look at the n+1 problem and what it looks like in practice. We'll also get an overview of efficient techniques to avoid the problem and improve performance.
GraphQL queries are made against a single endpoint. GraphQL servers like the Apollo Server allow the backend to define a GraphQL schema. The schema is a data model at the application layer that indicates how the client can query the database, just like a REST API contract.
When building a runtime in GraphQL, a unique resolver should be present for each discrete data type. The following example shows a sample Apollo Server setup, GraphQL schema and resolvers for different entity fields. The client can make calls against this runtime to get musician data, and also get album related data for the musician.
//Schema Definitions
const typeDefs = gql`
type Album {
id: ID!
title: String!
artistId: ID!
}
type Musician {
id: ID!
name: String!
albums: [Album]
}
type Query {
musicians: [Musician]
}
`;
//Resolver definitions
const resolvers ={
Query:{
musicians:()=>{
//Database/API call to get a list of musicians
return musicians;
},
},
Musician:{
albums:(musician)=>{
//Input is a single musician
//Database/API call to get a list of albums for this single musician
return albums
},
},
};
const server =newApolloServer({ typeDefs, resolvers });
server.listen(3000).then(({ url })=>{
console.log(`Starting new Apollo Server at ${url}`);
The client creates requests against the GraphQL server, tailored to include the information needed for display. In the server we have set up above, we have allowed clients the flexibility to request a list of musicians, and inside the musician resolver we are allowing the client to fetch associated albums per musician.
query {
musicians {
id,
name,
albums {
title
}
}
}
The client query above requests some musician related data and albums related data for each musician. We know how the schema and resolvers of this GraphQL server are designed, and the way the server will execute this query can lead to issues. In this case, the server will first fetch the list of musicians. Let’s say it finds n musicians in the database. For each musician found, the albums() resolver will be invoked to locate all the albums associated with that musician. This resolver will trigger a database call for each musician, which will be n calls. This means that in total, there will be n+1 database calls occurring. This is not very efficient and won’t scale after a point.
Consequences of the n+1 problem
The n+1 problem can lead to several client and server issues. The main problem is scaling, it might go unnoticed until a point, but as our application scales and n grows, the number of calls to the database or calls to the API that resolves our GraphQL fields will become unmanageable. Also, the latencies will start increasing, the product will behave inconsistently, pages without many nested calls will load quickly, while others that require nested data will be much slower. If you are using a cloud-based server like AWS or GCP to run the database, extra calls to the database will cost more in service fees and eventually all of it combined will lead to a very poor user experience. High latency reduces the ability to retain customers, and hence we should try to closely monitor it and fix the issues in our system as our application scales.
Since this is a common issue with GraphQL, there are well-established solutions for handling it. These include using Data Loaders or Batching.
Data loaders in GraphQL
Data loaders are a way to solve the n+1 problem. They can batch similar client GraphQL requests into a single query. Basically, consider them as an intermediate layer that converts multiple similar requests to a single batched request. For our case, instead of making n different requests for getting albums of each musician, data loaders will make one request send an array of musician ids and receive an array of album objects.
The implementation of data loaders depends on which version of GraphQL you are using. Some have built-in data loader functionality, like the java-dataloader for GraphQL Java.
GraphQL has a well maintained dataloader library that can be used in our GraphQL server. This utility mimics the original GraphQL calls with loaders passed to each resolver in the context value. The example below shows what the GraphQL query for musicians would look like with a data loader.
constDataLoader=require('dataloader');
// The dataloader takes in an array of musician ids and returns a promise that will return
// the album data for each musician.
const albumLoader =newDataLoader(musicianIds=>{
// DB call that accepts list of musicianIds.
returndatabaseCall(musicianIds)
// OR it can be an API call that accepts a list of musicianIds.
returnapiCall(musicianIds)
})
// Add the data loader to context
constcontext=async()=>{
const loaders ={
album:albumLoader(musicianIds),
// Create more loaders for other data that is nested in your schema
}
return loaders;
}
// Use this context in the Apollo Server definition to pass to each resolver when executed.
Once the data loader is set up and available in the context, the resolver should be updated to use that loader. The loader is only triggered once, for the list of musicians fetched, so the number of DB / API calls will be reduced to two.
Batching in GraphQL is an expansion of the data loader concept discussed in the previous section. Essentially, batching libraries provide ways to ensure that nested data is retrieved with fewer queries by defining how to group and load similar data. Note that the main GraphQL library also supports batch execution, which is a different concept about invoking multiple resolvers at once.
Promises are the key to how batching works in GraphQL. The request executes the appropriate resolver first, and tries to resolve all the requested fields. Where batch loaders are specified, the data is resolved as a promise. GraphQL Batch can then iterate through grouped data identifiers to fulfill the promises together by retrieving data with as few calls as the batch loader will allow.
There are many batch loaders to choose from, implemented in different languages. Choose the appropriate tool based on the language your client or server are implemented in. For those using Ruby, Shopify has produced an open source plugin to use. Ruby users could also use this popular open source plugin, while Javascript users can use an open source library.
Using the Shopify Ruby batching plugin, we can implement a batch loader on the server side.
First, define a custom loader that will be used to group database calls:
Next, apply the batching plugin to the GraphQL schema. It is advised that the plugin be defined after mutations so the batching can extend mutation fields and allow for cache clearing.
classMySchema<GraphQL::Schema
query MyQueryType
mutation MyMutationType
use GraphQL::Batch
end
Finally, use the batch loader class in the resolver with grouped identifiers to get a batch of nested data:
In this article, we have learned what is the n+1 problem in GraphQL and how the extra DB/API calls are not scalable and can lead to a poor user experience. GraphQL provides several options to streamline this issue like data loaders and batching which can be built directly into our server. If you want to use GraphQL for your application without having to build the entire server side from scratch, consider using Hygraph, a headless server that can host your data and provide you with a ready to use GraphQL API out of the box.
Blog Authors
Aagam Vadecha
Joanna Wallace
Share with others
Sign up for our newsletter!
Be the first to know about releases and industry news and insights.