0
0
NestJSframework~5 mins

DataLoader integration in NestJS

Choose your learning style9 modes available
Introduction

DataLoader helps reduce repeated database calls by batching and caching requests. It makes your NestJS app faster and more efficient.

When you have multiple requests for the same data in one operation.
When you want to avoid making many database calls for related data.
When using GraphQL resolvers that fetch nested or repeated data.
When you want to improve performance by caching repeated queries.
When you want to simplify data fetching logic by batching requests.
Syntax
NestJS
import DataLoader from 'dataloader';

const loader = new DataLoader(async (keys) => {
  // batch load function returns results in order of keys
  const results = await batchFetchFromDatabase(keys);
  return keys.map(key => results.find(r => r.id === key));
});

The batch load function receives an array of keys and must return results in the same order.

DataLoader caches results during a request to avoid duplicate fetches.

Examples
This example batches user ID requests and returns users in the same order.
NestJS
const userLoader = new DataLoader(async (userIds) => {
  const users = await userService.findByIds(userIds);
  return userIds.map(id => users.find(user => user.id === id));
});
Similar pattern for loading posts by IDs efficiently.
NestJS
const postLoader = new DataLoader(async (postIds) => {
  const posts = await postService.findByIds(postIds);
  return postIds.map(id => posts.find(post => post.id === id));
});
Sample Program

This example shows a NestJS service using DataLoader to batch user ID requests. It caches during one request scope and returns user names in order.

NestJS
import { Injectable, Scope } from '@nestjs/common';
import DataLoader from 'dataloader';

interface User {
  id: number;
  name: string;
}

@Injectable({ scope: Scope.REQUEST })
export class UserLoader {
  public readonly loader: DataLoader<number, User>;

  constructor(private readonly userService: UserService) {
    this.loader = new DataLoader(async (userIds: readonly number[]) => {
      const users = await this.userService.findByIds(userIds as number[]);
      return userIds.map(id => users.find(user => user.id === id));
    });
  }
}

// Usage in resolver or service
async function getUserNames(userLoader: UserLoader, ids: number[]) {
  const users = await Promise.all(ids.map(id => userLoader.loader.load(id)));
  return users.map(user => user?.name ?? 'Unknown');
}

// Mock UserService for demonstration
class UserService {
  private users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
    { id: 3, name: 'Carol' },
  ];

  async findByIds(ids: number[]): Promise<User[]> {
    // Simulate async DB call
    return this.users.filter(user => ids.includes(user.id));
  }
}

// Demo run
(async () => {
  const userService = new UserService();
  const userLoader = new UserLoader(userService);
  const names = await getUserNames(userLoader, [1, 2, 3]);
  console.log(names.join(', '));
})();
OutputSuccess
Important Notes

Use request-scoped providers in NestJS to create a new DataLoader instance per request.

Always return results in the same order as the keys array to avoid mismatches.

DataLoader caches only during one request; it does not cache across requests.

Summary

DataLoader batches and caches data fetching to improve performance.

Use it in NestJS with request-scoped providers for safe caching.

Always keep the batch function output order matching the input keys.