Next.js + サーバーサイドTypeScript + 関数フレーバーでクリーンなアプリを作ったので実装意図とか書く Advent Calendar 2022
の14日目。株式会社mofmofに生息しているshwldです。
前日はGraphQLリゾルバのディレクトリの構造について書きました
RelayStyleページネーションをPrismaで実装する
GraphQLでページネーションを実装する際の選択肢の一つとしてRelay Styleにするというものがあります。
MetaのRelayというGraphQLクライアントが定めている仕様で、これに合わせることでRelay Styleに対応したクライアントでページネーションの実装が楽になるので、これを採用しました。
自前で実装するのは大変なのかなと思ったのですが、graphql-relayの中に便利メソッドが生えていたので、割と簡単に実装できました。
利用するメソッドは以下です。
import { connectionFromArraySlice, cursorToOffset } from 'graphql-relay';
connectionFromArraySlice
は、スライスされた配列に開始位置と件数を渡すことで、仕様にあったconnectionを作ってくれます。簡単。
ちなみにconnectionFromArray
というメソッドもあり、こちらはスライスされる前の長い配列の中から開始位置、件数で該当の配列を切り出してconnectionを作ってくれるメソッドです。
今回はPrisma側で切り出し(カーソルいちから件数分の抽出)を行うため前者のconnectionFromArraySlice
を使います。
といっても難しいことはなく、以下の流れでできます。
- 受け取ったカーソル(connectionの
after
引数)をSQLのoffset
に変換する - 受け取った件数(connectionの
first
引数)をSQLのtake
に変換する - Prismaの
aggregate
で件数を取得する - Prismaの
findMany
にoffset
,take
を渡して結果を取得する - 結果と取得件数を、
connectionFromArraySlice
でconnectionに変換する
書いてて気づきましたが、この記事で扱う内容では、firstとafter以外のargumentに対応してないです。
import { connectionFromArraySlice, cursorToOffset } from 'graphql-relay';
import type { ConnectionArguments } from 'graphql-relay';
args: ConnectionArguments
const take = args.first
const skip = cursorToOffset(args.after)
const accounts = await db.account.findMany({ skip, take })
const totalCount = await db.account.aggregate({ _count: true });
const relayConnection = connectionFromArraySlice(accounts, args, {
sliceStart: skip,
arrayLength: totalCount._count,
});
できました。
参考コード: /use-cases/graphql-resolvers/src/shared/helpers/connection-helpers.ts
次回予告
明日はTurborepo使ってみたについて書きます。