12
2

More than 1 year has passed since last update.

NestJS + GraphQLでページネーションを実装してみた[要改良]

Posted at

概要

いちいちnodes, totalCount, hasNextPageとか書くのめんどくさいから
ジェネリクス使って汎用的に使えるようにしてみた
ベストプラクティスじゃないと思うので参考までに見てください!

こんな風にかける

    const query = {};
    query['id'] = productsInput.id;
    query['name'] = Like(`%${name}%`);

    const paginationQuery = new PaginationQuery<User>(
      query,
      this.userRepository, // Repository<User>
      limit,
      offset,
      ['posts', 'posts.comments'], // entityのリレーションも取得したいならここも書く
    );

    paginationQuery.setOrderCond({ updatedAt: 'DESC' }); // 任意

    const result = await paginationQuery.run();

コード

import {
  FindConditions,
  JoinOptions,
  ObjectLiteral,
  Repository,
} from 'typeorm';
import { EntityFieldsNames } from 'typeorm/common/EntityFieldsNames';

export class PaginationQuery<T> {
  private query:
    | string
    | ObjectLiteral
    | FindConditions<T>
    | FindConditions<T>[];
  private limit: number;
  private offset: number;
  private relations: string[];
  private repository: Repository<T>;
  private order: { [P in EntityFieldsNames<T>]?: 'DESC' | 'ASC' | 1 | -1 };
  private join: JoinOptions;

  constructor(
    query: string | ObjectLiteral | FindConditions<T> | FindConditions<T>[],
    repository: Repository<T>,
    limit?: number,
    offset?: number,
    relations?: string[],
  ) {
    this.query = query;
    this.limit = limit || 10;
    this.offset = offset || 0;
    this.relations = relations || [];
    this.repository = repository;
  }

  async setOrderCond(
    order: { [P in EntityFieldsNames<T>]?: 'DESC' | 'ASC' | 1 | -1 },
  ) {
    this.order = order;
  }

  async setJoinCond(join: JoinOptions) {
    this.join = join;
  }

  async run() {
    const [data, total] = await this.repository.findAndCount({
      where: this.query,
      take: this.limit,
      skip: this.offset,
      relations: this.relations,
      order: this.order,
      join: this.join,
    });

    let hasNextPage = true;
    if (data.length !== this.limit || total - (this.offset + this.limit) <= 0)
      hasNextPage = false;

    return {
      nodes: data,
      totalCount: total,
      hasNextPage: hasNextPage,
    };
  }
}

捕捉

Q. なんでorderとjoinはセッター使ってるの?

A.
[P in EntityFieldsNames<T>] こいつが問題児
例えば、createAt: "DESC"って書きたいんだけど上記の型エラーが出ちゃう
Tの中にcreateAtなんてないやんけ!的なやつ
なので、インスタンス生成してからセッタ経由でやったらうまくいったって話

まとめ

この辺りはtypeorm側で用意してくれたら嬉しいな〜っていうのと
joinしてからのリレーション先のカラムをwhereで使いたい時が上手くいかない
この辺りはうまい方法あったら教えて欲しいです

12
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
2