Next.js の getStaticProps で外部データソースからデータを取得! TypeScript で CSV ファイルを読み込む
TypeScript で Next.js の getStaticProps
を使う方法
Next.js の getStaticProps
は、静的なサイト生成のための重要な機能です。しかし、TypeScript を使うと型定義やデータ取得の処理が複雑になることがあります。
このガイドでは、TypeScript で getStaticProps
を効果的に使うためのステップバイステップな解説と、よくある落とし穴とその回避方法を紹介します。
ステップ 1:型定義
まず、getStaticProps
関数と返されるオブジェクトの型を定義する必要があります。これにより、開発中に型エラーを防ぎ、コードの信頼性を高めることができます。
import { GetStaticProps, GetStaticPropsContext } from 'next';
type Product = {
id: number;
name: string;
price: number;
};
export const getStaticProps: GetStaticProps<Product> = async ({ params }) => {
// ... データ取得処理
};
ステップ 2:データフェッチ
getStaticProps
関数内で、API やデータベースから必要なデータを取得します。非同期処理の場合は await
を使用し、Promise を返します。
import { GetStaticProps, GetStaticPropsContext } from 'next';
import { fetchProductById } from './api';
type Product = {
id: number;
name: string;
price: number;
};
export const getStaticProps: GetStaticProps<Product> = async ({ params }) => {
const productId = parseInt(params!.id);
const product = await fetchProductById(productId);
if (!product) {
return {
notFound: true,
};
}
return {
props: {
product,
},
revalidate: 60, // データ更新のチェック間隔(秒)
};
};
ステップ 3:コンポーネントへのデータ受け渡し
getStaticProps
関数で取得したデータは、ページコンポーネントに props
として渡されます。コンポーネント内で型情報に基づいてデータにアクセスできます。
import React from 'react';
interface ProductPageProps {
product: Product;
}
const ProductPage: React.FC<ProductPageProps> = ({ product }) => {
return (
<div>
<h1>{product.name}</h1>
<p>{product.price}</p>
</div>
);
};
export default ProductPage;
よくある落とし穴と回避方法
- リフレッシュポリシーの誤設定
revalidate
プロパティを使用して、データの更新頻度を適切に設定してください。 - データ取得の失敗
データ取得に失敗する可能性を考慮し、エラーハンドリングを実装しましょう。 - 非同期処理の誤った扱い
await
を正しく使用し、Promise を適切に返していることを確認してください。 - 型定義の漏れ
すべての変数とオブジェクトに適切な型を定義するようにしましょう。
TypeScript を使うことで、Next.js の getStaticProps
をより安全かつ効率的に利用することができます。型定義、データフェッチ、コンポーネントへのデータ受け渡し、そしてよくある落とし穴に注意することで、開発の生産性とコードの品質を向上させることができます。
pages/
- blog/
- index.tsx
- [slug].tsx
- _app.tsx
getStaticProps の実装
// pages/blog/index.tsx
import { GetStaticProps } from 'next';
import { fetchBlogPosts } from '../../api/blog';
import { BlogPost } from '../../types';
export const getStaticProps: GetStaticProps<BlogPost[]> = async () => {
const blogPosts = await fetchBlogPosts();
return {
props: {
blogPosts,
},
};
};
ページコンポーネント
// pages/blog/index.tsx
import React from 'react';
import BlogPostList from '../../components/BlogPostList';
import { BlogPost } from '../../types';
interface BlogIndexProps {
blogPosts: BlogPost[];
}
const BlogIndex: React.FC<BlogIndexProps> = ({ blogPosts }) => {
return (
<div>
<h1>ブログ記事一覧</h1>
<BlogPostList blogPosts={blogPosts} />
</div>
);
};
export default BlogIndex;
データ型定義
// types/blog.ts
export type BlogPost = {
id: number;
title: string;
content: string;
createdAt: Date;
};
API データフェッチ
// api/blog.ts
import { fetch } from 'node-fetch';
export const fetchBlogPosts = async () => {
const response = await fetch('https://your-api-endpoint/blog-posts');
const data = await response.json();
return data as BlogPost[];
};
コンポーネント
// components/BlogPostList.tsx
import React from 'react';
import BlogPost from './BlogPost';
import { BlogPost } from '../../types';
interface BlogPostListProps {
blogPosts: BlogPost[];
}
const BlogPostList: React.FC<BlogPostListProps> = ({ blogPosts }) => {
return (
<ul>
{blogPosts.map((post) => (
<BlogPost key={post.id} post={post} />
))}
</ul>
);
};
export default BlogPostList;
個別記事ページ
// pages/blog/[slug].tsx
import React from 'react';
import { useRouter } from 'next/router';
import { fetchBlogPostBySlug } from '../../api/blog';
import { BlogPost } from '../../types';
interface BlogPostPageProps {
blogPost: BlogPost | null;
}
const BlogPostPage: React.FC<BlogPostPageProps> = ({ blogPost }) => {
const router = useRouter();
if (!blogPost) {
return <div>記事が見つかりませんでした。</div>;
}
return (
<div>
<h1>{blogPost.title}</h1>
<p>{blogPost.content}</p>
<p>投稿日: {blogPost.createdAt.toLocaleString()}</p>
</div>
);
};
export const getStaticProps: GetStaticProps<BlogPostPageProps> = async ({ params }) => {
const slug = params!.slug;
const blogPost = await fetchBlogPostBySlug(slug);
return {
props: {
blogPost,
},
};
};
API データフェッチ(個別記事)
// api/blog.ts
export const fetchBlogPostBySlug = async (slug: string) => {
const response = await fetch(`https://your-api-endpoint/blog-posts/${slug}`);
const data = await response.json();
return data as BlogPost | null;
};
説明
getStaticProps
を使って、ブログ記事のリストデータを静的に取得します。- ページコンポーネントでは、取得したデータを
BlogPostList
コンポーネントに渡して表示します。
getStaticProps
は、動的なパスとパラメータを使用して、個々のページのデータを取得することができます。これにより、ブログ記事の詳細ページのような、URL に基づいて異なるコンテンツを表示するページを作成できます。
// pages/blog/[slug].tsx
export const getStaticProps: GetStaticProps<BlogPostPageProps> = async ({ params }) => {
const slug = params!.slug;
const blogPost = await fetchBlogPostBySlug(slug);
return {
props: {
blogPost,
},
};
};
データのフィルタリングとソート
getStaticProps
内で、取得したデータをフィルタリングやソートして、特定の条件に合致するデータのみを返すことができます。
// pages/blog/index.tsx
export const getStaticProps: GetStaticProps<BlogPost[]> = async () => {
const blogPosts = await fetchBlogPosts();
// 最新の記事のみを返す
const recentPosts = blogPosts.filter((post) => {
const now = new Date();
const oneMonthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
return post.createdAt >= oneMonthAgo;
});
return {
props: {
blogPosts: recentPosts,
},
};
};
外部データソースからのデータ取得
getStaticProps
は、API やデータベースだけでなく、CSV ファイルや JSON ファイルなどの外部データソースからデータを取得することもできます。
// pages/products/index.tsx
import { readFileSync } from 'fs';
import { parse } from 'csv-parse/lib/sync';
import { Product } from '../../types';
export const getStaticProps: GetStaticProps<Product[]> = async () => {
const productsData = readFileSync('data/products.csv', 'utf8');
const records = parse(productsData, { columns: true });
const products: Product[] = records.map((record) => {
return {
id: parseInt(record.id),
name: record.name,
price: parseFloat(record.price),
};
});
return {
props: {
products,
},
};
};
キャッシュの無効化
revalidate
プロパティを使用して、getStaticProps
で取得したデータのキャッシュを無効化することができます。これにより、データが更新されたときに、新しいデータが自動的に再取得されます。
// pages/blog/index.tsx
export const getStaticProps: GetStaticProps<BlogPost[]> = async () => {
const blogPosts = await fetchBlogPosts();
return {
props: {
blogPosts,
},
revalidate: 60, // 1 分ごとにデータの更新をチェック
};
};
エラーハンドリング
getStaticProps
内でエラーが発生した場合は、notFound
または redirect
プロパティを使用して、適切なエラーハンドリングを行うことができます。
// pages/blog/[slug].tsx
export const getStaticProps: GetStaticProps<BlogPostPageProps> = async ({ params }) => {
const slug = params!.slug;
try {
const blogPost = await fetchBlogPostBySlug(slug);
return {
props: {
blogPost,
},
};
} catch (error) {
console.error(error);
return {
notFound: true,
};
}
};
getStaticProps
は、Next.js で静的なサイト生成を実現するための強力なツールです。
typescript next.js