概要
Next.js と GraphQL を組み合わせた開発において、コードの保守性と再利用性を高める重要な手法として「Fragment colocation」があります。
この記事では、Fragment colocation とは何か、そしてどのようにして実装するかについて解説します。
Fragment colocationとは
Fragment colocation とは、GraphQL の Fragment をコンポーネントと同じファイル内に定義し、そのコンポーネントが必要とするデータの形を明確に示す手法です。
Fragment は再利用可能なクエリの一部分を定義するもので、これをコンポーネントと同じ場所に配置することで、コンポーネントとデータの結びつきを強くします。
Fragment colocationの利点
- データ依存関係の明確化:コンポーネントが必要とするデータが明確になる
- コード変更の局所化:コンポーネントの変更時にデータ要件も同時に更新できる
- オーバーフェッチの防止:コンポーネントが必要とするデータのみを取得できる
- 型安全性の確保:型定義が自動生成され、型エラーを事前に検出できる
その他詳細は以下の記事などをご参考ください🙋♂️
実装例
それでは Next.js で実装していきます。
コンポーネントとFragment定義
まず、PostContent(記事表示)コンポーネントとそれに必要なFragmentを定義します。
src/components/ui/PostContent/index.tsx
import React from 'react'
import { PostFragment } from '@/app/graphql'
import styles from './PostContent.module.css'
import BackButton from '@/components/ui/BackButton'
import MainTitle from '@/components/ui/MainTitle'
import FormattedDate from '@/components/ui/DateFormatter'
import Image from 'next/image'
import { gql } from '@apollo/client'
type PostContentProps = {
post: PostFragment
}
export const POST_FRAGMENT = gql`
fragment post on Post {
title
content
thumbnailUrl
createdAt
}
`
export default function PostContent({ post }: PostContentProps) {
return (
<article className={styles.container}>
<div className={styles.header}>
<BackButton>
← 戻る
</BackButton>
<MainTitle>{post.title}</MainTitle>
<FormattedDate date={post.createdAt} />
</div>
{post.thumbnailUrl && (
<div className={styles.thumbnailContainer}>
<Image
src={post.thumbnailUrl}
alt={post.title}
fill
className={styles.thumbnail}
priority
unoptimized={true}
/>
</div>
)}
<div className={styles.content}>
{post.content}
</div>
</article>
)
}
-
POST_FRAGMENTという名前でFragmentを定義し、エクスポートしています -
Fragmentには記事表示に必要な最小限のフィールドを含める-
title,content,thumbnailUrl,createdAt
-
- コンポーネントの
propsの型としてPostFragmentを使用-
Codegenによって、自動生成された型
-
クエリでのFragmentの使用
次に、このFragmentを実際のクエリで使用します:
src/graphql/queries/getPost.ts
import { gql } from '@apollo/client';
import { POST_FRAGMENT } from '@/components/ui/PostContent/index';
export const GET_POST = gql`
query GetPost($id: ID!) {
${POST_FRAGMENT}
published {
post(id: $id) {
...postFragment
}
}
}
`;
- コンポーネントからエクスポートされた
POST_FRAGMENTをインポートしています - クエリ内で
Fragmentを参照するために${POST_FRAGMENT}でFragmentの定義を含める -
...postFragmentでFragmentを展開しています
まとめ
便利ですね。
特に、型安全の保証、実装変更時の保守性の向上はかなり恩恵があるので、積極的に活用していきたいです。