Using Apollo Server 4 to Solve the N+1 Problem with Data Loaders

Introduction to the N+1 Problem and Data Loaders

In this lesson, we’re going to address a common performance issue in GraphQL known as the N+1 problem and how to solve it using Data Loaders.

The N+1 problem occurs when your GraphQL server makes an excessive number of database or API calls to satisfy nested queries. For example, if you fetch a list of books along with their authors, your server might make one query to get the books (1 query) and then one additional query per book to get the author (N queries), leading to a total of N+1 queries. This can significantly degrade the performance of your application.

Data Loaders help to batch and cache the requests, effectively reducing the number of queries made and improving performance.

Benefits of Using Data Loaders:

  • Batching: Combines multiple requests into a single batch query.
  • Caching: Reduces redundant queries by remembering previously fetched results.

Defining the GraphQL Schema

In GraphQL, the schema defines the shape of the data and the queries you can perform. To illustrate how Data Loaders can solve the N+1 problem, we’ll create a simple GraphQL schema with authors and books.

Here’s how you can define the schema:

import { ApolloServer } from '@apollo/server';

// Sample data
const authors = [
  { id: '1', name: 'J.R.R. Tolkien' },
  { id: '2', name: 'J.K. Rowling' }
];
const books = [
  { id: '1', title: 'The Hobbit', author: '1' },
  { id: '2', title: 'Harry Potter', author: '2' }
];

// Define schema
const typeDefs = `#graphql
  type Author {
    id: ID!
    name: String!
  }

  type Book {
    id: ID!
    title: String!
    author: Author
  }

  type Query {
    books: [Book]
    author(id: ID!): Author
  }
`;

In this schema:

  • We define Author and Book types.
  • The Book type has a nested Author type.
  • The Query type fetches a list of books and a single author by ID.

Data Loaders Primary Functions

Data Loaders serve two primary functions: batching and caching requests.

  • Batching: Data Loaders collect multiple requests made in a single event loop tick and combine them into a single query, reducing the total number of database/API calls.

For example, consider the difference:

SELECT id, name FROM books WHERE id in (1, 2, 3, ..., 100);

versus

SELECT id, name FROM books WHERE id = 1;
SELECT id, name FROM books WHERE id = 2;
SELECT id, name FROM books WHERE id = 3;
...
SELECT id, name FROM books WHERE id = 100;
  • Caching: Once a piece of data is fetched, Data Loaders cache the result. If the same data is requested again, the Data Loader returns the cached value instead of making another request.
Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal