📍
Next.js + Prisma をコロケーションの考え方で書く
Next.js の App Router によって React のコンポーネントから直接 Prisma クライアントを用いて SQL のクエリを実行することが現実的になりました。
このときにコロケーションの1つである GraphQL Fragment と同じ考え方で Prisma のクエリを実行できることに気付いたので紹介します。
サンプルコード
次のリポジトリにサンプルコードがあります。
モデル
今回の説明に使用するモデルです。
この Post モデルを表示するコンポーネントの実装について考えます。
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
愚直な実装
全部の Post をその Post の author の名前と共に表示するコンポーネントを実装すると次のような形になると思います。
PostList.tsx
import { Post, User } from "@prisma/client";
type Props = {
posts: (Post & { author: User })[];
};
export const PostList = ({ posts }: Props) => {
return (
<div>
<h1>Post List</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
{post.author.name}: {post.title}
</li>
))}
</ul>
</div>
);
};
親コンポーネントからは次のように使用します。
export default async function Home() {
const posts = await prisma.post.findMany({ include: { author: true } });
return (
<div>
<PostList posts={posts} />
</div>
);
}
この実装だと posts
の型定義が子コンポーネントにあるのに対して、データを取得するクエリは親コンポーネントにあるため責務が分散してしまっています。
Fragment を参考にした実装
prisma.post.findMany
の引数に渡す { include: { author: true } }
から返り値の推論ができると、クエリの指定と型定義を子コンポーネントに実装することができそうです。
実は Prisma にはこのような型推論を行うための Utility Types が存在します。
これを使用すると先程の子コンポーネントは次のように実装することができます。
PostList2.tsx
export const getPostsQuery = Prisma.validator<Prisma.PostDefaultArgs>()({
include: { author: true },
});
type Post = Prisma.PostGetPayload<typeof getPostsQuery>;
type Props = {
posts: Post[];
};
export const PostList2 = ({ posts }: Props) => {
return (
<div>
<h1>Post List</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
{post.author.name}: {post.title}
</li>
))}
</ul>
</div>
);
};
親コンポーネントでは getPostsQuery
を import してクエリを発行します。
export default async function Home() {
const posts2 = await prisma.post.findMany(getPostsQuery);
return (
<div>
<PostList2 posts={posts2} />
</div>
);
}
まとめ
GraphQL Fragment を参考に Prisma でもクエリと型定義をコンポーネントに集約する方法を紹介しました。
Discussion