Skip to content

ISoft-Data-Systems/type-graphql-dataloader

 
 

Repository files navigation

TypeGraphQL-DataLoader

TypeGraphQL-DataLoader is an utility to use DataLoader with TypeGraphQL without fuss.

Why does this fork exist?

The original library seems to be abandon-ware these days, but it is a very useful glue between type-graphql, typeorm, and @apollo/server. However, the version of the original library doesn't support modern versions of apollo/graphql/etc. So this fork modernizes all dependencies.

Breaking changes

  • Switched to ESM
  • Compatible with graphql 16
  • Compatible with apollo 5
  • Compatible with express 4 or 5
  • Compatible with type-graphql 2.x

Install

Because the upstream package is now abandon-ware, the package can be pulled directly from github using the following npm command:

npm install github:@isoft-data-systems/type-graphql-dataloader

This will create an installation from github instead of from the node package manager.

The latest build is tested with the following packages:

  • type-graphql 2
  • @apollo/server 5
  • (optional) typeorm 0.3

Getting Started

Apollo Server is the first-class supported server. If your application uses Apollo Server, pass ApolloServerLoaderPlugin() as a plugin when instantiating the server. This plugin is for set-up and clean-up against each request.

import { ApolloServerLoaderPlugin } from "type-graphql-dataloader";
import { DataSource } from "typeorm";

// Instantiate modern typeorm datasource, connect it to the database
const dataSource = new DataSource({/* Your database connection details here */})

const apollo = new ApolloServer({
  schema,
  plugins: [
    ApolloServerLoaderPlugin({
      typeormGetConnection: () => dataSource),
    }),
  ],
});

...

With TypeORM

TypeORM is the first-class supported ORM. If your application uses TypeORM with TypeGraphQL, adding @TypeormLoader decorator to relation properties will solve N + 1 problem. When the fields are accessed by graphQL, batch loading will be performed using DataLoader under the hood.

import { ObjectType, Field, ID } from "type-graphql";
import { TypeormLoader } from "type-graphql-dataloader";
import { Entity, PrimaryGeneratedColumn, ManyToOne, RelationId } from "typeorm";
import { User } from "./User";

@ObjectType()
@Entity()
export class Photo {
  @Field((type) => ID)
  @PrimaryGeneratedColumn()
  id: number;

  @Field((type) => User)
  @ManyToOne((type) => User, (user) => user.photos)
  @TypeormLoader()
  user: User;
}
import { ObjectType, Field, ID } from "type-graphql";
import { TypeormLoader } from "type-graphql-dataloader";
import { Entity, PrimaryGeneratedColumn, OneToMany, RelationId } from "typeorm";
import { Photo } from "./Photo";

@ObjectType()
@Entity()
export class User {
  @Field((type) => ID)
  @PrimaryGeneratedColumn()
  id: number;

  @Field((type) => [Photo])
  @OneToMany((type) => Photo, (photo) => photo.user)
  @TypeormLoader()
  photos: Photo[];
}

@TypeormLoader does not need arguments since v0.4.0. In order to pass foreign key explicitly, arguments are still supported. Take a look at previous README for details.

With Custom DataLoader

It is possible to assign custom DataLoader to a field by adding @Loader decorator to the corresponding method with @FieldResolver. @Loader takes a batch load function which is passed to the DataLoader constructor. The decorated method should return a function which takes a DataLoader instance and returns Promise of loaded value(s).

import DataLoader from "dataloader";
import { groupBy } from "lodash";
import { Resolver, Query, FieldResolver, Root } from "type-graphql";
import { Loader } from "type-graphql-dataloader";
import { getRepository, In } from "typeorm";
import { Photo } from "./Photo";
import { User } from "./User";

@Resolver((of) => User)
export default class UserResolver {

  ...

  @FieldResolver()
  @Loader<number, Photo[]>(async (ids, { context }) => {  // batchLoadFn
    const photos = await getRepository(Photo).find({
      where: { user: { id: In([...ids]) } },
    });
    const photosById = groupBy(photos, "userId");
    return ids.map((id) => photosById[id] ?? []);
  })
  photos(@Root() root: User) {
    return (dataloader: DataLoader<number, Photo[]>) =>
      dataloader.load(root.id);
  }
}

About

TypeGraphQL + DataLoader + TypeORM made easy

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 99.6%
  • JavaScript 0.4%