ブログコンテンツの移行:ContentfulからNext.jsの静的Markdownへ

📅 2025-08-12 ✍️ prosalmontech #開発ノート#Next.js#Contentful#Markdown#データ移行

ContentfulのようなヘッドレスCMSからNext.jsプロジェクトの静的Markdownファイルへブログコンテンツを移行することは、デプロイの簡素化や外部依存関係の削減といった利点をもたらします。この投稿では、Contentfulからデータを抽出し、静的Markdown形式に変換するプロセスを詳しく説明します。

Contentfulのデータ構造を理解する

移行の前に、Contentfulでブログデータがどのように構造化されているかを理解することが重要です。主要なフィールドには通常、以下が含まれます。
- タイトル: 投稿のタイトル。
- 公開日: 投稿が公開された日付。
- スラッグ: 投稿のURLフレンドリーな識別子。
- 説明/抜粋: 投稿の簡単な要約。
- タグ: 投稿に関連付けられたカテゴリまたはキーワード。
- コンテンツ: 投稿の本文。多くの場合、リッチテキスト形式です。

Contentfulからデータを取得する

Contentfulは、コンテンツを取得するためのContent Delivery APIを提供しています。Space IDとAccess Tokenが必要です。

GET https://cdn.contentful.com/spaces/{SPACE_ID}/environments/master/entries?access_token={ACCESS_TOKEN}&content_type=pageBlogPost&order=-fields.publishedDate&select=fields.title,fields.publishedDate,fields.slug,fields.shortDescription,fields.content,fields.tags

コンテンツに関する重要な注意点: Contentfulのリッチテキストフィールドは、プレーンなMarkdownやHTMLではなく、特定のJSON形式でコンテンツを返します。contentフィールドがプレーンテキストの場合、埋め込み画像や複雑な書式設定が含まれていない可能性があります。リッチテキストの場合、通常はHTMLやMarkdownに変換するためにライブラリ(例:@contentful/rich-text-html-renderer)が必要になります。私たちの場合、contentフィールドはプレーンテキストであったため、変換が簡素化されました。

Markdown形式への変換

各Contentfulエントリは、YAMLフロントマターを持つMarkdownファイルに変換する必要があります。

フロントマターの構造:
フロントマターは、Markdownファイルの先頭にあるYAMLブロックで、メタデータを含みます。

title: "あなたの投稿タイトル"
date: "YYYY-MM-DD"
description: "あなたの投稿の簡単な説明。"
tags: ["タグ1", "タグ2"]
---

コンテンツ本文:
投稿の本文はフロントマターの後に続きます。Contentfulのコンテンツがすでにプレーンテキストであれば、そのまま使用できます。適切な改行が維持されていることを確認してください。

移行の自動化

Node.jsスクリプトでこのプロセスを自動化できます。
1. Contentfulから関連するすべてのエントリを取得します。
2. 各エントリを反復処理します。
* フロントマターのデータを抽出します。
* ファイル名にはslugを使用します。
* Markdownファイルの内容(フロントマター+本文)を構築します。
* 目的のディレクトリ(例:src/app/blog/en/)にファイルを書き込みます。

スクリプトの例(概念):

const fs = require('fs');
const path = require('path');
// ... (Contentfulデータ配列) ...

posts.forEach(post => {
  const frontMatter = `---
title: ${JSON.stringify(post.frontMatter.title)}
date: ${post.frontMatter.date}
description: ${JSON.stringify(post.frontMatter.description)}
tags: [${post.frontMatter.tags.map(tag => JSON.stringify(tag)).join(', ')}]
---
`;

  const markdownContent = `${frontMatter}

${post.content}`;
  const filePath = path.join(postsDirectory, post.lang, `${post.slug}.md`);
  fs.writeFileSync(filePath, markdownContent, 'utf8');
});

画像の処理

Contentfulのリッチテキストフィールドに埋め込まれた画像は、テキストコンテンツの一部ではありません。それらは別のアセットです。
- 課題: APIを介して画像を直接取得し、Markdownに埋め込むには、リッチテキストJSONの解析、アセットURLの取得、画像のダウンロード、そしてMarkdownの更新が必要です。
- 実用的な解決策: よりシンプルなセットアップの場合、画像を手動でダウンロードしてpublic/imagesディレクトリに配置し、Markdownで相対パスを使用して参照できます(例:![代替テキスト](/images/あなたの画像.jpg))。

これらの手順に従うことで、ブログコンテンツを動的なCMSから静的なファイルベースのシステムに正常に移行し、Next.jsのデプロイを簡素化できます。

🚀 prosalmontech ポートフォリオ

SatViewer3D、QuakeViewer3D、NihongoChatAI、JPQuizなど、開発中のアプリ・Webサービスを公開中!

トップページでプロダクトを見る