AstroとmicroCMSとCloudflare Pagesで無料ブログ制作

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/66e71a6d-971f-1ed4-4e6e-64c06252b564.png)


## はじめに
こんにちは。
オークファン開発部デザイナーの@af_etoです。

最近[Astro](https://astro.build/)に触れる機会があり、その開発の手軽さに感動しておりました。
またヘッドレスCMSの[microCMS](https://microcms.io/)との連携もとてもシンプルで、
動的コンテンツはmicroCMSに任せ、Astro側でmicroCMSのAPIを取得することで、
ウェブサイトの高速化とUXの向上が期待できます。

本稿ではAstroとmicroCMS、さらに[Cloudflare Pages](https://pages.cloudflare.com/)を組み合わせ、
コンテンツ更新をトリガーにした「ブログ自動デプロイ設計」のプロセスをご紹介します。

ブログと言っても書き残したいことは特に無いので、
行き場を失くした写真を活かして、写真ブログを作成しました。

## 画面設計
レイアウトは写真が縦に並んだシンプルなものにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/19be792e-33f4-5bac-459e-f7b4702e1999.png)

## Astroのセットアップ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/0cd4c11c-6632-88d0-459f-cc704d9254d8.png)

Astroのローカル環境を作るところから始めます。
下記のような前提条件があるので注意です。

* **Node.js** - v18.14.1 またはそれ以上。
* **テキストエディタ** - VS Codeと公式Astro拡張機能を推奨します。
* **ターミナル** - Astroは、コマンドラインインターフェイス（CLI）からアクセスします。

### プロジェクトの作成
プロジェクト名を「untitled」で、Astro用のディレクトリを作成/移動します。

```terminal
$ mkdir untitled
$ cd untitled
```

### インストールウィザードの起動
```terminal
$ npm create astro@latest
```

いくつか質問されるので回答していきます。
`Astroが必要なのでダウンロードしてもいいですか？`は、`y`を入力します。

```terminal
Need to install the following packages:
  create-astro@4.5.2
Ok to proceed? (y) 
```

`どこにプロジェクトを作りますか？`は、untitled/なので`./`を入力。

```terminal
   dir   Where should we create your new project?
         ./capricious-conjunction
```

`このプロジェクトにサンプルファイルを含めますか？`は、
推奨となっている`Include sample files`を選択します。

```terminal
  tmpl   How would you like to start your new project?
         ● Include sample files (recommended)
         ○ Use blog template 
         ○ Empty 
```

`依存ファイルをインストールしますか？`は、特別な理由が無い限り`Yes`を選択。

```terminal
  deps   Install dependencies? (recommended)
         ● Yes  ○ No 
```

`TypeScriptで書きますか？`には、`Yes`を選択しておきます。

```terminal
    ts   Do you plan to write TypeScript?
         ● Yes  ○ No 
```

`TypeScriptはどの程度厳密であるべきか?`は、推奨となっている`Strict`を選択。

```terminal
   use   How strict should TypeScript be?
         ● Strict (recommended)
         ○ Strictest 
         ○ Relaxed 
```

`Gitリポジトリにしますか？`は、`Yes`を選択します。

```terminal
   git   Initialize a new git repository? (optional)
         ● Yes  ○ No 
```

以上で質問は終わりです。


```terminal
  next   Liftoff confirmed. Explore your project!
         Run npm run dev to start the dev server. CTRL+C to stop.
         Add frameworks like react or tailwind using astro add.

         Stuck? Join us at https://astro.build/chat

╭─────╮  Houston:
│ ◠ ◡ ◠  Good luck out there, astronaut! 🚀
╰─────╯
```

セットアップが完了しました。
プロジェクトディレクトリは以下の構成です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/6dfc658d-9fe0-2392-5d2d-84cd39b9619c.png)


### 開発用サーバー起動

```terminal
$ npm run dev
```

開発用のサーバーが立ち上がり、開発用URLができます。
[http://localhost:4321/](http://localhost:4321/)

```
> dev
> astro dev

  🚀  astro  v3.6.4 started in 182ms
  
  ┃ Local    http://localhost:4321/
  ┃ Network  use --host to expose
```

開発用URLをブラウザで表示すると、
「Welcome to Astro」というタイトルのサンプルページを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/31552f28-a9ec-68e5-ff1f-bce16d08beb9.png)

## Astroのファイル構成
`src`ディレクトリにはプロジェクトのソースコードが格納されています。

* `src/layouts`
複数のページで共有されるUI構造を定義するためのAstroコンポーネント。

* `src/pages`
サイト上に新しいページを作成するために使用される、特別な種類のコンポーネント。

* `src/components`
HTMLページで再利用可能なコンポーネント。

ざっくり表現すると構成イメージはこんな感じでしょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/7285dcd6-23a8-35ed-f124-2346acdaec52.png)

上記のはじめから用意されているコードは使用せずに、
既存の`src/pages/index.astro`を上書きする形でHTMLを書いていきます。

```html:src/page/index.astro
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Untitled</title>
</head>
<body>
    <header class="header">
        <h1 class="title"><a href="/">Untitled</a></h1>
    </header>
    <div class="frame">
        <main class="main">
            <div class="wrap">
                <ul class="posts">
                    <li><div class="cut"><img src="/assets/img/sample.jpg" alt=""></div></li>
                    <li><div class="cut"><img src="/assets/img/sample.jpg" alt=""></div></li>
                    <li><div class="cut"><img src="/assets/img/sample.jpg" alt=""></div></li>
                </ul>
            </div>
        </main>
        <div class="pagination">
            <a class="prev" href="">preview</a>
            <a class="next" href="">next</a>
        </div>
        <footer class="footer">
            <div class="copy">© 2023 Untitled</div>
        </footer>
    </div>
</body>
</html>
```

Astroのファイル構成へ書き換えます。

```html:src/pages/index.astro
---
import Layout from '../layouts/Layout.astro';
---

<Layout>
    <main class="main">
        <div class="wrap">
            <ul class="posts">
                <li><div class="cut"><img src="/assets/img/sample.jpg" alt=""></div></li>
                <li><div class="cut"><img src="/assets/img/sample.jpg" alt=""></div></li>
                <li><div class="cut"><img src="/assets/img/sample.jpg" alt=""></div></li>
            </ul>
        </div>
    </main>
    <div class="pagination">
        <a class="prev" href="">preview</a>
        <a class="next" href="">next</a>
    </div>
</Layout>

<style>
/* ここにスタイルを記述する */
</style>
```

```html:src/layouts/Layout.astro
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Untitled</title>
</head>
<body>
    <header class="header">
        <h1 class="title"><a href="/">Untitled</a></h1>
    </header>
    <div class="frame">
        <slot />
        <footer class="footer">
            <div class="copy">© 2023 Untitled</div>
        </footer>
    </div>
</body>
</html>

<style>
/* ここにスタイルを記述する */
</style>
```

`Layout.astro`に書かれた`<slot />`の中に`index.astro`が読み込まれます。
CSSをHTMLと同じastroファイル内に記述すれば、レイアウトは完成です。

ちなみにSCSSを書くためにはSASSのパッケージをインストールしましょう。

```terminal
$ npm install sass
```

`<style>`タグに`lang="scss"`という属性を付ければSCSSが使用できます。

```html
<style lang="scss">
```

![画面収録 2023-12-08 0.53.18.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/967f3b02-bcdf-a95e-6e64-8c6672f61e1c.gif)

画面設計が完了しました。
上記の通り写真はダミー画像のままなので、
microCMSに写真をアップロードして、APIで写真データを取得します。

## microCMSの設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/b0192e18-db34-aa8c-7053-216d86bedddd.png)

[https://microcms.io/](https://microcms.io/)

### アカウントの作成
アカウントがない場合は作成しましょう。
料金プランは無料の「Hobby」でひとまず問題ありません。

### サービスを作成
ログイン後は左メニューの`+`ボタンより新たにサービスを作成していきます。
サービスを`一から作成する`を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/b165a516-fa19-c0ce-4cb0-2a8faddc8e2f.png)

### サービス情報の入力
`サービス名`と`サービスID`は自由に入力できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/ed243609-fb64-7d8a-ef11-fc08808d3f5b.png)

### サービス作成完了
サービスが作成されたので、`サービスにアクセスする`を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/aa360f76-2874-f44e-c3e1-cb40739c4ac0.png)

### APIを作成
ブログのテンプレートもあるのですが、`自分で決める`を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/ffc89141-ecc6-f87f-549a-8ff79757f2a2.png)

### APIの基本情報を入力
`API名`と`エンドポイント`も自由に入力できます。
`エンドポイント`は後で使用するので覚えておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/25b4cddd-2690-7b8c-58b7-34d4d71faad1.png)

### APIの型を選択
JSON配列を返却する`リスト形式`を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/c169c2aa-3ce0-6a63-5626-a92016ed00d8.png)

### APIスキーマを定義
入力画面の項目を設定します。今回は写真ブログなので
フィールドID：`photo`
表示名：`写真`
種類：`画像`
のみを用意します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/fbbc9627-22eb-0a13-e873-bdda45e4e483.png)

### APIの作成完了
コンテンツを作成する準備が整いました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/226d0144-eada-34ba-7404-486636c41cb2.png)

写真を3点追加しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/44a3b024-4eb2-6bd6-2641-56b540c515aa.png)

自分でデータベースやAPIを用意しなくても、
microCMSにアクセスすればブログデータを取り出せるようになりました。

## AstroとmicroCMSの接続

AstroとmicroCMSを簡単に連携させるため、
`microCMS JavaScript SDK`というプログラムをプロジェクトにインストールします。

```
$ npm i microcms-js-sdk
```

連携させるためのファイルを作成していきます。

### API呼び出し設定

#### 環境変数を設定

Astroのプロジェクトルートに.envファイルを作成します。
`MICROCMS_SERVICE_DOMAIN`には、サービス名、
`MICROCMS_API_KEY`にはAPIキーを入れます。

```env:.env
MICROCMS_SERVICE_DOMAIN=untitled-blog
MICROCMS_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

APIキーはmicroCMSのページ左上歯車⚙️マークから表示される
`サービス設定：APIキー`ページからコピーできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/5815b772-0161-9e0f-af45-a8c78d8bce18.png)

#### microcms.tsファイルの作成

`src/libs/`にmicroCMSに関するファイル`microcms.ts`を作成し、
ブログデータを取得する関数を書いていきます。
エンドポイントは設定した`blogs`を入力します。

```ts:src/libs/microcms.ts
// SDK利用準備
import { createClient } from 'microcms-js-sdk';
import type {
    MicroCMSListResponse,
    MicroCMSQueries,
    MicroCMSImage,
} from 'microcms-js-sdk';

// 型定義
export type Blogs = {
    photo: {
        url: string;
        image: MicroCMSImage;
    };
};
export type BlogsResponse = MicroCMSListResponse<Blogs>;

// clientの作成
const client = createClient({
    serviceDomain: import.meta.env.MICROCMS_SERVICE_DOMAIN,
    apiKey: import.meta.env.MICROCMS_API_KEY,
});

// 'blogs' APIからIDを指定して個別データを取得する関数
export const getBlogs = async (queries?: MicroCMSQueries) => {
    return await client.get<BlogsResponse>({ endpoint: 'blogs', queries });
};
```

写真を`index.astro`で呼び出します。

```html:src/pages/index.astro
---
import Layout from '../layouts/Layout.astro';
// microCMS呼び出し
import { getBlogs } from '../libs/microcms';

const data = await getBlogs({ limit: 100 });
const blogs = data.contents;
---

<main class="main">
    <div class="wrap">
        <ul class="posts">
            {blogs.map((blog) => {
                return (
                    <li>
                        <div class="cut">
                            <img src={blog.photo.url} />
                        </div>
                    </li>
                );
            })}
        </ul>
    </div>
</main>
<div class="pagination">
    <a class="prev" href="">preview</a>
    <a class="next" href="">next</a>
</div>
```

microCMSでアップロードした写真が確認できました！

![画面収録 2023-12-08 0.40.37.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/d286d115-412f-5afe-27c8-a9875abdbee9.gif)

## ページ送りも実装する

今のところコンテンツはが3点なので、
ひとまず1データ1ページ（`pageSize: 1`）で確認します。

```html:src/pages/[...page].astro
---
import Layout from '../layouts/Layout.astro';
import { getBlogs } from '../libs/microcms';
import type { GetStaticPathsOptions } from 'astro';

export const getStaticPaths = async ({ paginate }: GetStaticPathsOptions) => {
    const blogs = await getBlogs({ limit: 100 });
    return paginate(blogs.contents, { pageSize: 10 });
};
const { page } = Astro.props;
---

<Layout>
    <main class="main">
        <div class="wrap">
            <ul class="posts">
                {page.data.map((blog) => {
                    return (
                        <li>
                            <div class="cut">
                            <img src={blog.photo.url} />
                            </div>
                        </li>
                    );
                })}
            </ul>
        </div>
    </main>
    <div class="pagination">
        {page.url.prev && (
            <a class="prev" href={page.url.prev}>preview</a>
        )}
        {page.url.next && (
            <a class="next" href={page.url.next}>next</a>
        )}
    </div>
</Layout>
```

ブログのほとんどの場合、トップページは`/1`というURLではなく`/`にしたいと思うので、
レストパラメーターを使ってファイル名を`[...page].astro`としておきます。

![画面収録 2023-12-08 0.32.43 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/f1586e58-0035-2752-c273-ecc5a1705343.gif)

ページ送りも実装できました！
これでmicroCMSとAstroの連携が完了です。

## GitHubとCloudflare Pagesの連携

GitHubからのCloudflare Pagesへの自動デプロイ設定を作ります。

### リポジトリの作成
GitHubにプロジェクトのリポジトリを作成し、ローカルファイルをpushします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/19fbcd44-f98d-1dfc-1829-d90de8a9d2a7.png)

### Cloudflare Pages設定
こちらもアカウントが必要です。
無料プランで問題ありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/59969294-50bf-40b9-b562-11641888bcc4.png)

### Cloudflare Pages設定
左メニューの`Workers & Pages：概要`から`アプリケーションの作成`ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/a7802120-e0c7-7b6f-20a6-17bbd12b815a.png)

`Pages`タブの`Gitに接続`ボタンから連携していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/b9ee7340-dcd8-cd9f-1b88-c2fe280cdc60.png)

連携するGitHubリポジトリが表示されていない場合は、
GitHubからCloudflare Pagesにリポジトリアクセス権限を与えてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/6c1694fa-126c-314e-7f5e-398168018a49.png)

アクセス権限を与えると該当のリポジトリを選択できます。
`セットアップの開始`ボタンを押して進みます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/7101bec8-7c0d-6b54-f424-b146d5489eab.png)

プロダクションブランチは`master`ブランチを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/59aba10e-2b9e-8445-fb2c-25251622dc4c.png)

フレームワークプリセットは`Astro`を選択します。
`ビルドコマンド`、`出力先ディレクトリ`はプリセットのままで構いません。

#### 環境変数の設定
環境変数はプロジェクトルートに作成していた`.env`ファイルを参照して入力します。
入力が完了したら「保存してデプロイする」ボタンを押します。

```env:.env
MICROCMS_SERVICE_DOMAIN=untitled-blog
MICROCMS_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/9c66fd74-8610-9f86-d9e4-23a68b86ed04.png)

「成功しました！」というメッセージとデプロイ先のURLが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/f9a93d8f-74a0-1174-df45-312d9328fd50.png)

## microCMSとCloudflare Pagesの連携

microCMSからCloudflare Pagesへの自動デプロイ設定を作ります。

### Cloudflare Pagesのアプリケーション設定
アプリケーションの`設定`の左メニュー`ビルド＆デプロイ`ページを表示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/96bca79a-9b6a-01fb-8fcf-7f170a166a5e.png)

### デプロイフック

ページ下部の`デプロイフック`項目にある`デプロイフックを追加します`ボタンを押してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/b13a4519-f760-0eba-9c51-d1b13b29cdcc.png)

デプロイフック名を入力して、ブランチを選択して`追加`ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/3ae1dcac-ef65-2355-6b33-83cfcdfda053.png)

デプロイフックのURLが生成されますので、コピーしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/64664a60-f1fb-caae-a099-2bcbab6f466a.png)

### microCMSのAPI設定
microCMSのAPI画面の右上`API設定`から`Webhook`ページを開き`追加`ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/cd4dcae1-5068-7553-a717-7663cd786835.png)

### サービスの選択
Cloudflare Pagesを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/5309a687-c0bb-c9f3-e938-950cc86c368e.png)

webhookの識別名を入力し、デプロイフックのURLをペーストします。
以下の通知タイミングの設定はそのままでも問題ありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/17c1000a-dfc9-b3dc-6b13-91b126867164.png)

動作確認として、新たなコンテンツを追加/公開します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/da7043f8-6324-5cc5-dad1-0972b31cf4fc.png)

ビルドされ、新しい写真が表示されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2301766/d779439e-323d-4d8c-ea67-54f94b8b64a8.png)

[https://untitled-4ne.pages.dev/](https://untitled-4ne.pages.dev/)

## おわりに
比較的、簡単にブログを構築することができました。

プロジェクトの要件に基づいて、導入を検討するのもちろんですが、
例えばコーポレートサイトの「お知らせ」など、
静的サイトの一部にだけ動的コンテンツを導入したい場合では、
AstroとヘッドレスCMSの構成は非常に実用的であると思われます。

是非、Astroを利用したウェブサイトを作ってみてください。
わかりやすい[公式ガイド](https://docs.astro.build/ja/getting-started/)も用意されています。

### 参考記事
* [Astroを使ってサイトを構築するためのガイド、リソース、APIリファレンス](https://docs.astro.build/ja/getting-started/)
* [AstroとmicroCMSでつくるブログサイト](https://blog.microcms.io/astro-microcms-introduction/)
* [AstroとmicroCMSで作ったサイトにページネーションを実装する](https://zenn.dev/chabatake_i/articles/astro-microcms)
* [imgixによる画像サイズの出し分けをpictureタグで対応する](https://blog.microcms.io/imgix-picture/)