stock.dev
Astro × microCMS × Cloudflare Pagesで闘病ブログを作った話
cl

Astro × microCMS × Cloudflare Pagesで闘病ブログを作った話

·13 min read

0歳でウエスト症候群と診断された息子の記録を残すために、個人ブログを立ち上げた。 Claude Codeのキャッチアップをしたくて一緒に作ることにした。(初めて触る) ホスティングからデプロイ自動化まで、決めたことと詰まったところをまとめる。 公開URL: https://west-blog.pages.dev/


1. 計画

  • ゴール: ClaudeCodeを使って実用性のあるブログを作る
    • ①microCMSで記事を公開・更新するとWebhook経由で自動再デプロイ
    • ②フロントはAstroを使い、Lighthouse Performance100点を目指す
  • 技術はAstro + microCMS + Cloudflareで指定。
  • デザインは書体と色を指定して10案生成してもらって選択。
    • 闘病記録という性質に合わせて、オフホワイト + セージグリーンのアクセント色。本文はNoto Serif JP(手紙のような落ち着き)、UIはNoto Sans JP。

2. 技術と選定理由

役割 採用 理由
フレームワーク Astro5 静的生成(SSG)に強くTypeScriptも標準対応
ヘッドレスCMS microCMS 日本語の管理UIと直感的なリッチエディタ。記事・タグの参照型が綺麗
ホスティング Cloudflare Pages 無料枠で十分、Git連携で自動デプロイ
サイトマップ生成 @astrojs/sitemap sitemap-index.xmlを自動生成
microCMS SDK microcms-js-sdk TypeScriptの型サポート付きでAPIを叩ける

3. ファイル構成

microcms-astro-blog/
├── public/
│   ├── favicon.svg                  # サイトファビコン
│   ├── og-default.png               # OGP デフォルト画像 (1200×630)
│   └── robots.txt                   # Sitemap: 〜 を記述
├── src/
│   ├── components/
│   │   ├── Header.astro             # ロゴ + ナビ
│   │   ├── Footer.astro             # コピーライト
│   │   ├── ArticleCard.astro        # 記事カード (全面クリック対応)
│   │   ├── Pagination.astro         # ページネーション
│   │   ├── TagList.astro            # タグ一覧セクション
│   │   └── SEO.astro                # title/description/OGP/canonical
│   ├── layouts/
│   │   └── BaseLayout.astro         # 共通レイアウト
│   ├── lib/
│   │   └── microcms.ts              # microCMSクライアント + ヘルパー
│   ├── pages/
│   │   ├── index.astro              # トップ (記事一覧 1ページ目)
│   │   ├── page/[page].astro        # 記事一覧 2ページ目以降
│   │   ├── articles/[id].astro      # 記事詳細 (microCMS contentId)
│   │   ├── tags/
│   │   │   ├── index.astro          # タグ一覧
│   │   │   └── [tag]/
│   │   │       ├── index.astro      # タグ別 1ページ目 (slug)
│   │   │       └── page/[page].astro
│   │   ├── about.astro              # このブログについて
│   │   └── 404.astro
│   ├── styles/
│   │   ├── global.css               # CSS 変数・リセット
│   │   └── prose.css                # 記事本文用タイポグラフィ
│   ├── types/
│   │   └── microcms.ts              # Article / Tag の型
│   └── consts.ts                    # サイト名・ヒーロー文言など
├── astro.config.mjs                 # site URL + sitemap integration
├── tsconfig.json                    # extends astro/tsconfigs/strict
├── package.json
├── .env.example                     # 環境変数の雛形
├── .gitignore
└── PLAN.md                          # 仕様書 (随時更新)

src/lib/microcms.ts

microCMSとの通信を全部ここに集約。 ページ側はgetArticles() getTags() extractExcerpt() tagPath()だけ呼ぶ

src/types/microcms.ts

Article/Tagの型を1か所で定義

src/consts.ts

サイトタイトル、ヒーローテキスト、1ページあたりの記事数などの定数

src/styles/global.css

配色やフォントサイズのCSS変数。 各コンポーネントの<style>からvar(--accent)のように参照


4. ヘッドレスCMSとしてのmicroCMSの復習

  • データ(記事・タグ)の入稿と保管はmicroCMS側
  • フロントエンド(HTML/CSS/JSの見た目)はAstro側で組む
  • 両者はHTTP API + JSONで連携 WordPressと違い、microCMSは見た目の提供はない。 代わりにhttps://{サービスドメイン}.microcms.io/api/v1/articlesのようなAPIエンドポイントが提供され、そこにAPIキーつきでGETするとJSONで記事リストが返ってくるという仕組み。

4-1. SDKでAPIを呼ぶ

microcms-js-sdkを使うと、API呼び出しを型付きで書ける。

src/lib/microcms.ts
import { createClient } from 'microcms-js-sdk';
import type { MicroCMSQueries } from 'microcms-js-sdk';
import type { Article, Tag } from '../types/microcms';
const client = createClient({
  serviceDomain: import.meta.env.MICROCMS_SERVICE_DOMAIN,
  apiKey: import.meta.env.MICROCMS_API_KEY, // 読み取り権限のキー
});
 
// 記事一覧 (リスト形式のAPIなのでgetList)
// client.getListはmicroCMSの関数。 
// `articles`というAPIから一覧データを取ってきてねという意味。
export const getArticles = (queries?: MicroCMSQueries) =>
  client.getList<Article>({ endpoint: 'articles', queries });
  
// 記事詳細 (リスト中の1件を取りに行く)
export const getArticleDetail = (contentId: string) =>
  client.getListDetail<Article>({ endpoint: 'articles', contentId });
  
// タグ一覧
export const getTags = (queries?: MicroCMSQueries) =>
  client.getList<Tag>({ endpoint: 'tags', queries });

MicroCMSQueriesにはmicroCMSのAPI仕様にあるフィルタ・ソート・件数指定などが型付きで入る。

await getArticles({
  limit: 10,
  offset: 20,
  orders: '-day',                          // day 降順
  filters: `tags[contains]${tag.id}`,      // 特定タグの記事だけ
});

4-2. SSG(静的生成)でビルド時に呼ぶ

Astroは標準でSSGなので、上記のawait getArticles(...)npm run buildのタイミングで実行され、その結果HTMLが生成される。 本番サイトは静的HTMLだけが配信されるので、

  • 速い(CDNからHTMLを返すだけ)
  • APIキーがブラウザに漏れない(ビルド時にしか使われない)
  • DBを持たない・SSR用のサーバーが要らない というメリットがある。

4-3. 動的ルートはgetStaticPaths

記事詳細(/articles/{id}/)やタグ別ページ(/tags/{slug}/)のような動的URLは、AstroのgetStaticPathsでビルド時に全パターンを列挙する。

src/pages/articles/[id].astro
---
export const getStaticPaths = (async () => {
  const { contents } = await getArticles({ limit: 100 });
  return contents.map((article) => ({
    params: { id: article.id },
  }));
}) satisfies GetStaticPaths;
const { id } = Astro.params;
const article = await getArticleDetail(id!);
---

これでmicroCMSにある記事1つずつにつきHTMLが1ファイル作られる。


5. 自分で設定した部分(microCMS / Cloudflare)

ここはコードに残らない、管理画面での作業の備忘録。

5-1. microCMSのサービス作成とAPI設計

サービス作成

  1. https://microcms.io/ で会員登録 (※すでに済んでいた)
  2. 「サービスを作成」→ サービス名 / サブドメイン名(=MICROCMS_SERVICE_DOMAIN)を決める。サブドメインは後から変えられない。
  3. 完成するとダッシュボードへ。

API #1: tags(先にこちらから作る)

参照元として先に必要なので、articlesより前に作る。

  1. ダッシュボードで「API(一覧) → +追加」
  2. API名: tags、エンドポイント: tags、APIの型: リスト形式
  3. APIスキーマ
フィールドID 表示名 種類 必須
name 表示名 テキストフィールド
slug スラッグ テキストフィールド
  1. 作成後、最低でも1件「公開」しておく(参照型のドロップダウンに出てこないため)

API #2: articles

  1. 同様にarticles APIを リスト形式 で作成
  2. スキーマ
フィールドID 表示名 種類 必須
title タイトル テキストフィールド
body 本文 リッチエディタ
day 記録日 日付
eyecatch アイキャッチ 画像
tags タグ コンテンツ参照 - 複数 (tags)
slug スラッグ テキストフィールド
  • 「コンテンツ参照 - 複数」 の参照先でtagsを選ぶには、tags APIがすでに存在し、最低1件のコンテンツが入っている必要がある

APIキーの発行

  1. ダッシュボード左メニュー「APIキー
  2. +追加」→ デフォルト権限の 「GET(取得)」だけON、他はOFF
  3. 発行されたキーがMICROCMS_API_KEY
  4. 絶対にGitにコミットしない.envに書いて.gitignoreで除外

Webhook(自動デプロイ用)

これはCloudflare PagesのDeploy Hookを作った後に設定する(後述)。

5-2. GitHubにリポジトリを作成

  1. ローカルでgit init → 初回コミット
  2. gh CLIが入っているならgh repo create microcms-astro-blog --private --source=. --remote=origin --push
  3. 入っていない場合はGitHubのWebで空リポジトリを作ってgit remote add origin ...git push -u origin main

5-3. Cloudflare Pagesのセットアップ

プロジェクト作成

  1. Cloudflareダッシュボード → Workers & Pages+ 作成
  2. 上部のタブで 「Pages」 を選択
  3. Gitに接続」→ GitHubアカウントで対象リポジトリを選択

ビルド設定

項目
プロジェクト名 west-blog(これがhttps://west-blog.pages.devのURLになる)
本番ブランチ main
フレームワークプリセット Astro
ビルドコマンド npm run build
ビルド出力ディレクトリ dist

環境変数

「環境変数」セクションで 本番(Production) に対して3つ登録:

名前 種類
MICROCMS_SERVICE_DOMAIN west-blog プレーンテキスト
MICROCMS_API_KEY (APIキー) シークレット(暗号化)
PUBLIC_SITE_URL https://west-blog.pages.dev プレーンテキスト
APIキーは必ずシークレットを選択。=ビルドログにも値が出ない。

Pagesの環境変数は1か所登録でOK。ビルド時もランタイム時も同じ値を読みます。 Cloudflare Workers のほうは「ビルド変数」と「ランタイム変数」が別物なので注意。

「保存してデプロイ」

設定完了後、pushのたびに自動でビルド → デプロイされる。 初回ビルドは2〜3分でした。 完了するとhttps://{プロジェクト名}.pages.devで見られるようになる。

5-4. microCMS Webhook ↔ Cloudflare Deploy Hook

記事を更新すれば自動で再ビルドされるように設定した。

Cloudflare側でDeploy Hookを発行

  1. Pagesプロジェクト → 「設定 → ビルド」
  2. 下のほうの「デプロイフック」で「+ 追加」
  3. フック名(例: microcms-update)、ブランチmainを選んで作成
  4. 払い出されたURL(https://api.cloudflare.com/...で始まる)をコピー

microCMS側でWebhook登録

  1. microCMSダッシュボード → articles API → 「API設定 → Webhook」
  2. 「+ 追加」→ 「カスタム通知」または「Cloudflare Pages」
  3. 先ほどコピーしたURLを貼り付け
  4. 通知タイミングで「コンテンツの公開時」「コンテンツの更新時」「コンテンツの削除時」をチェック
  5. 保存
  6. tags APIでも同じ手順 で登録(タグ更新時も再ビルドさせるため) これでmicroCMSで「公開」ボタンを押すと、数秒以内にCloudflare Pagesのビルドが始まり、数分後に本番に反映されます。

6. もう一つのゴール:パフォーマンス

今回の挑戦では2つのゴールがあった。

  • microCMSを更新したらで自動デプロイできるブログサイトを作る
  • フロントはAstroを使い、Lighthouse Performance100点を目指す

後者のパフォーマンスのあれこれは別ブログにアウトプットした。 # Astro + microCMSのブログをLighthouse56→100点にした話

7. 最後に

今回は初めてClaude Codeと一緒にサイトを作ってみるというチャレンジをした。

セッションが長くなってくると、コンテキストが圧迫されて話が通じなくなったり出力の質が悪くなるという話を聞いていたが、以下を行うことで特にゴールまで困ることなく並走してもらえた。

- 最初にプランモードで計画を立てる
- 仕様や進捗をPLAN.mdに書き、チェックポイントごとに自動で参照・追記させる

CLAUDE.mdはセッションの最初に読み込まれた後、書かれている内容が必ず守ってもらえるとは限らないとのことだったので、仕様や進捗を管理するPLAN.mdを別途作った。一つ一つ順を追って小さく進んでくれたので、前に構築した部分のコードが壊れることもなかった。

作り始めてからLighthouseの点数改善が終わるまでで約25時間ほどかかった。