Skip to content

Dependency Injection friendly adaptation of dataloaders for NestJS.

License

Notifications You must be signed in to change notification settings

nestjs-devkit/dataloader

Repository files navigation

Dataloader

Dependency Injection friendly adaptation of graphql/dataloader for NestJS.

This package internally uses AsyncLocalStorage to provide a clean cache map for each dataloader in each request, but it may have a slight negative impact on the performance.

Installation

npm i @nestjs-devkit/dataloader

Tutorial

Defining DataLoaders

To define a dataloader, we simply extend the abstract DataLoader and implement the resolve() method.

import { DataLoader } from "@nestjs-devkit/dataloader";
// ... more imports

@Injectable()
export class UserIdLoader extends DataLoader<number, User> {
  constructor(@InjectRepository(User) private repo: EntityRepository<User>) {}

  protected async resolve(keys: number[]): Promise<User[]> {
    return this.repo.find({ id: { $in: keys } });
  }
}

Preparing DataLoaders

Before using the dataloaders, we need to firstly provide these dataloaders.

@Module({
  providers: [UserIdLoader],
  exports: [UserIdLoader],
})
export class UsersModule {}

Besides, the DataLoaderModule is also required for them to work.

@Module({
  imports: [DataLoaderModule.forRoot()],
})
export class AppModule {}

Using Dataloaders

Now we can use the dataloaders simply by injecting them into your resolvers.

@Resolver(() => Book)
export class BooksResolver {
  constructor(private userIdLoader: UserIdLoader) {}

  @ResolveField()
  async owner(@Parent() book: Book) {
    return this.userIdLoader.load(book.owner.id);
  }
}

Remember to import the module where the dataloader is defined.

@Module({
  imports: [forwardRef(() => UsersModule)],
})
export class BooksModule {}

Advanced

Cross-Platform

There is a DataLoaderContext used to provide clean cache maps for dataloaders in each request, which need to be applied by a middleware.

By default, a built-in middleware will be automatically applied to apply the context for Express. You can disable it and apply it on your own for other platforms.

@Module({
  imports: [DataLoaderModule.forRoot({ middleware: false })],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer): void {
    // Express (Default)
    consumer.apply(DataLoaderContextMiddleware).forRoutes("*");
    // Fastify
    consumer.apply(DataLoaderContextMiddleware).forRoutes("(*)");
  }
}

Contributing

Pull requests are always welcome, but please open an issue firstly before major changes. ;]