Closed6

Next.js x NestJS x GraphQLでページングを実装する

mu-sukemu-suke

開発ステップ

  1. NestJSでのページング対応
  2. Next.jsでのページング対応
  3. 無限スクロールのライブラリを使ってUI作成
mu-sukemu-suke

GraphQL ページネーションの代表的な実装手法

GraphQLのページネーション実装方法で有名なもので以下の2つが存在する

  1. Offset-basedページネーション
  2. Cursor-basedページネーション

今回は2を採用する

mu-sukemu-suke

ページネーションと無限スクロール、その有用性について

そもそもWebにおいて無限スクロールってあんまり見ないなと思ったので調査。

結論

別ウィンドウでnページ以降を表示する方法と無限スクロール機能を使って同一ページ内にnページ以降の内容を表示をする方法の2つがあるが、Googleはどちらも推奨している。

ただ、サイトの特性によってはどちらがユーザーにとって使いやすいかは分かれるのでユーザー行動を適宜解析することが重要

https://seolaboratory.jp/41951/

mu-sukemu-suke

GraphQLの型をジェネリクスで指定できないのどうやって突破しようかな

import { Type } from '@nestjs/common'
import { Field, InputType, ObjectType } from '@nestjs/graphql'

@ObjectType()
export class PageInfo {
  @Field()
  endCursor: string

  @Field()
  hasNext: boolean
}

type Edge<T> = {
  cursor: string
  node: T
}

type IPaginatedType<T> = {
  pageInfo: PageInfo
  edges: Edge<T>[]
}

@InputType()
export class PaginatedInput {
  @Field()
  first: number

  @Field()
  after: string
}

export function Paginated<T>(classRef: Type<T>): Type<IPaginatedType<T>> {
  @ObjectType(`${classRef.name}Edge`)
  abstract class EdgeType {
    @Field()
    cursor: string

    @Field()
    node: T
  }

  @ObjectType({ isAbstract: true })
  abstract class PaginatedType implements IPaginatedType<T> {
    @Field()
    pageInfo: PageInfo

    @Field()
    edges: EdgeType[]
  }
  return PaginatedType as Type<IPaginatedType<T>>
}
Undefined type error. Make sure you are providing an explicit type for the "node" of the "EdgeType" class.
mu-sukemu-suke

解決した。

修正箇所としては以下

import { Type } from '@nestjs/common'
import { Field, InputType, ObjectType } from '@nestjs/graphql'

@ObjectType()
export class PageInfo {
  @Field()
  endCursor: string

  @Field()
  hasNext: boolean
}

type Edge<T> = {
  cursor: string
  node: T
}

type IPaginatedType<T> = {
  pageInfo: PageInfo
  edges: Edge<T>[]
}

@InputType()
export class PaginatedInput {
  @Field()
  first: number

- @Field()
+ @Field({ nullable: true })
  after: string
}

export function Paginated<T>(classRef: Type<T>): Type<IPaginatedType<T>> {
  @ObjectType(`${classRef.name}Edge`)
  abstract class EdgeType {
    @Field()
    cursor: string

-   @Field()
+   @Field(() => classRef)
    node: T
  }

  @ObjectType({ isAbstract: true })
  abstract class PaginatedType implements IPaginatedType<T> {
    @Field()
    pageInfo: PageInfo

-   @Field()
+   @Field(() => [EdgeType], { nullable: 'items' })
    edges: EdgeType[]
  }
  return PaginatedType as Type<IPaginatedType<T>>
}

勘所としては以下

  • nullableを指定する際の注意点
    • 対象が配列ならばitems or itemsAndList
    • 配列でなければboolean
  • ジェネリクスを使用している場合はGraphql側に型を教えてあげる必要があるためreturnTypeFunctionを記載する
このスクラップは2022/07/29にクローズされました