😐

「microCMS + Astro + Cloudflare Pagesで爆速ブログを30分で構築する」とか言うけど意外と詰まった話(前編)

に公開

はじめに

「microCMS + Astro + Cloudflare Pages」で検索すると、「30分で爆速ブログ構築!」みたいな記事がよく出てきて、無料だしサイコー!みたいな感じになっているのですが、サイトのどこのボタンを押せばいいのよと意外と詰まったのでスクショ多めで備忘録を残します。

ちなみに、出来上がりのサイトはこのような感じです。

https://my-microcms-blog.pages.dev

環境

  • M1 Mac
  • Node.js v20.x
  • Astro 4.x
  • microCMS
  • Cloudflare Pages

ゴール

microCMSで記事管理ができ、microCMSで記事を公開すると自動でgithub側にもアクションを起こし、更新がブログに反映されるようにします。

と言われても、よくわからないよってなると思います。
高速に表示するための仕組みが以下のようになっていて、注文を受けてから料理を作るのではなくて、注文前に料理を作っておいてそれを出すような構造になっています。下の図を見るとmicroCMSとユーザーの間に色々入っていますが、microCMSで変更すれば、ビルドしてすぐに見せれる状態まで、つまり料理を作るところまでやっておく感じです。

引用元:https://blog.microcms.io/what-is-headlesscms/

全体の流れ

  • microCMSの登録
  • Astroを自分のPCにコピーしてくる
  • コピーしてきたAstroをgitと連携
  • Cloidflare Pagesでホスティング
  • 自動デプロイ設定

1. microCMSのセットアップ

1-1. アカウント作成

https://microcms.io/ でアカウント作成。メール認証を済ませます。

1-2. サービス作成

「一から作成する」を選びます。

次にサービス名を決めます。このサービス名は後々まで使うので、控えておきましょう。今回は「n-s-blog」としてみました。

これがサービスドメインになります。(n-s-blog.microcms.io)。

1-3. API作成

サービスを作成したら次にAPIを作成します。
「自分できめる」を選びましょう。

今回は以下のように設定しています。

  • API名: ブログ
  • エンドポイント: blogs
  • APIの型: リスト形式(重要)

1-4. フィールド設定

次にAPIのスキーマを設定します。
APIでやりとりする内容みたいなものです。
追加ボタンから追加していきます。

以下のフィールドを追加:

フィールドID 表示名 種類 必須
title タイトル テキストフィールド
content 本文 リッチエディタ
thumbnail サムネイル 画像 -

1-5. テストコンテンツ作成

適当に2〜3記事作って「公開」します。
先ほど追加したサムネイルの項目にはO-DANから写真を持ってきました。

テスト用なので仮の内容で右上の「公開」を押します。

1-6. APIキー取得

「API設定」→「APIキー」でデフォルトのAPIキーをコピー。
これも後々使うので控えておきましょう。

2. Astroプロジェクト作成

ここからは「ターミナル」での作業となります。
自分がいるディレクトリにastroのプロジェクトフォルダができるので、自分がフォルダを作成したいディレクトリに移動しておきましょう。

npm create astro@latest my-microcms-blog
cd my-microcms-blog

対話式の質問が以下のようにくるので基本yesで答えていきます。

3. microCMS SDKインストール

npm install microcms-js-sdk

4. 環境変数設定

ここまでのインストールを終えた時点で、プロジェクトの構造を確認してみます。

# プロジェクトに移動
cd my-microcms-blog
# ディレクトリ構造の確認
bashtree -L 2 -I node_modules

こんな感じになっているはずです:

.
├── README.md
├── astro.config.mjs
├── package.json
├── public/
├── src/
│   ├── env.d.ts
│   └── pages/
├── tsconfig.json
└── package-lock.json

ここからプロジェクトのルートに.envファイルを作成していきます。

.envファイルを作成:

touch .env

viやvim, vscodeなどでファイルを編集しましょう。
以下を書き込みます。

MICROCMS_SERVICE_DOMAIN=my-blog
MICROCMS_API_KEY=your-api-key-here

ついでに、.env.gitignoreに入ってるか確認もしましょう。

echo ".env" >> .gitignore

5. microCMSクライアント設定

ここからAstroとmicroCMSと繋ぐためのファイルを作っていきます。

src/lib/microcms.tsを作成:

mkdir src/lib
touch src/lib/microcms.ts
src/lib/microcms.ts
import { createClient } from 'microcms-js-sdk';

if (!import.meta.env.MICROCMS_SERVICE_DOMAIN) {
  throw new Error('MICROCMS_SERVICE_DOMAIN is required');
}

if (!import.meta.env.MICROCMS_API_KEY) {
  throw new Error('MICROCMS_API_KEY is required');
}

export const client = createClient({
  serviceDomain: import.meta.env.MICROCMS_SERVICE_DOMAIN,
  apiKey: import.meta.env.MICROCMS_API_KEY,
});

export type Blog = {
  id: string;
  createdAt: string;
  updatedAt: string;
  publishedAt: string;
  revisedAt: string;
  title: string;
  content: string;
  thumbnail?: {
    url: string;
    height: number;
    width: number;
  };
};

export type BlogResponse = {
  contents: Blog[];
  totalCount: number;
  offset: number;
  limit: number;
};

6. レイアウト作成

これはWebページで使うレイアウト設定となります。

mkdir src/layouts
touch src/layouts/Layout.astro
src/layouts/Layout.astro
---
interface Props {
  title: string;
  description?: string;
}

const { title, description = 'microCMS + Astro + Cloudflare Pages' } = Astro.props;
---

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content={description} />
    <title>{title}</title>
    <style is:global>
      body {
        font-family: -apple-system, sans-serif;
        max-width: 800px;
        margin: 0 auto;
        padding: 2rem 1rem;
        line-height: 1.8;
      }
      img {
        max-width: 100%;
        height: auto;
      }
    </style>
  </head>
  <body>
    <slot />
  </body>
</html>

7. トップページ作成

src/pages/index.astro
---
import Layout from '../layouts/Layout.astro';
import { client } from '../lib/microcms';
import type { BlogResponse } from '../lib/microcms';

const data = await client.get<BlogResponse>({
  endpoint: 'blogs',
  queries: {
    orders: '-publishedAt',
    limit: 100,
  },
});
---

<Layout title="ブログ一覧">
  <h1>My Blog</h1>
  
  <ul style="list-style: none; padding: 0;">
    {data.contents.map((blog) => (
      <li style="margin-bottom: 2rem; border-bottom: 1px solid #eee; padding-bottom: 1rem;">
        <h2>
          <a href={`/blog/${blog.id}`}>
            {blog.title}
          </a>
        </h2>
        {blog.thumbnail && (
          <img 
            src={blog.thumbnail.url} 
            alt={blog.title}
            loading="lazy"
          />
        )}
        <time datetime={blog.publishedAt}>
          {new Date(blog.publishedAt).toLocaleDateString('ja-JP')}
        </time>
      </li>
    ))}
  </ul>
</Layout>

8. 詳細ページ作成

ここは注意が必要で、詳細ページのファイル名が[id].astroなので、シェルで特殊文字として解釈され流ため、'で区切ってあげる必要があります。

mkdir src/pages/blog
# NG
touch src/pages/blog/[id].astro

# OK
touch 'src/pages/blog/[id].astro'
src/pages/blog/[id].astro
---
import Layout from '../../layouts/Layout.astro';
import { client } from '../../lib/microcms';
import type { Blog, BlogResponse } from '../../lib/microcms';

export async function getStaticPaths() {
  const data = await client.get<BlogResponse>({
    endpoint: 'blogs',
    queries: {
      limit: 100,
    },
  });

  return data.contents.map((blog) => ({
    params: { id: blog.id },
  }));
}

const { id } = Astro.params;

const blog = await client.get<Blog>({
  endpoint: 'blogs',
  contentId: id,
});
---

<Layout title={blog.title}>
  <nav>
    <a href="/">← 一覧に戻る</a>
  </nav>
  
  <article>
    <h1>{blog.title}</h1>
    <time datetime={blog.publishedAt}>
      {new Date(blog.publishedAt).toLocaleString('ja-JP')}
    </time>
    
    {blog.thumbnail && (
      <img src={blog.thumbnail.url} alt={blog.title} />
    )}
    
    <div set:html={blog.content} />
  </article>
</Layout>

9. ローカル確認

まずはローカルで動くか確認します。

npm run dev

http://localhost:4321 で動作確認。

このように記事が表示されればOKです。


いかがでしたでしょうか?
長くなってきたので、前編はここまでとして、後編でCloudflareへのデプロイを行なっていきます。

後編はこちら!
https://zenn.dev/nishina__n/articles/65a204680bcb68

参考

Discussion