🐱

[NestJS + typeorm + GraphQL] PaginationQuery実装してみた

2 min read

概要

いちいち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,
    };
  }
}

捕捉

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

まとめ

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

Discussion

ログインするとコメントできます