# Astroでブログを作る手順：ヘッドレスCMS連携とSSG・更新反映

公開日: 2026-10-20 | 更新日: 2026-10-20 | URL: https://blog.genko.me/astro-blog-cms

> Astroでブログを作る方法を、genkoのSDKで記事一覧と詳細を静的生成する例で解説します。APIキーの管理、画像配信、Content Collectionsとの使い分け、Webhook再ビルド、下書きプレビューまでまとめます。

AstroでヘッドレスCMSのブログを作るには、ビルド時に公開記事を取得し、`getStaticPaths()`で記事詳細のHTMLを生成します。genkoではSDKの`getAll()`で全件を取得でき、公開後の変更はWebhookから再ビルドする構成にできます。

本記事はgenko.meの運営側が執筆しています。2026年10月20日時点の[Astro導入ガイド](https://docs.genko.me/frameworks/astro/getting-started)とSDK実装をもとに、まず静的ブログを作る方法を紹介します。

## AstroのブログはMarkdownとヘッドレスCMSのどちらで作る？

開発者だけで書くブログなら、記事とコードを同じリポジトリで管理する方法も候補になります。編集者がブラウザで原稿を更新するなら、CMSに本文を置き、Astroを表示と配信に使う構成が合います。

| 構成 | 向いている運用 | 更新の反映 |
| --- | --- | --- |
| ローカルMarkdownとContent Collections | Gitで記事をレビューしたい | リポジトリ更新後のビルド |
| CMSとSDKで静的生成 | 編集者がブラウザで記事を書く | CMS更新後の再ビルド |
| CMSとリクエスト時レンダリング | 公開直後に取得する必要がある | サーバー取得とキャッシュ方針による |

SDKを直接呼びます。Content Collectionsはローカル・外部コンテンツをコレクションとして扱う選択肢ですが、本記事のために導入する必要はありません。複数ソースを共通の検証ルールで扱うときに、独自ローダーの保守も含めて検討します。

## Astroでブログを作る前に用意するもの

1. 既存のAstroプロジェクトを用意します。
2. [genko.me](https://genko.me/)でワークスペースとリスト形式のAPI `blog`を作ります。
3. `title`をテキスト、`body`をリッチテキストとして登録し、記事を保存して公開します。
4. `blog`の読み取りを許可したAPIキーを発行し、ビルド環境に`GENKO_API_KEY`を設定します。

CMS側の操作は[クイックスタート](https://docs.genko.me/getting-started)を参照してください。サンプルの`your-workspace`は実際のワークスペースIDへ置き換えます。

```bash
npm install @genko-me/sdk
```

APIキーを`PUBLIC_`付きの変数へ入れたり、ブラウザで動くスクリプトへ渡したりしないでください。静的生成ならキーが必要なのはビルド環境で、完成したHTMLを配るサーバーにキーを持たせる必要はありません。

## SDKで記事の型と接続先を定義する

次の2ファイルを作ります。型だけを書いても管理画面にフィールドは作られません。CMS側のフィールドID・種類を同じにしてから接続します。

```ts
// src/lib/schema.ts
import { listApi } from "@genko-me/sdk";

export type Blog = { title: string; body: string };
export const apis = {
  blog: listApi<Blog>({
    fields: [
      { id: "title", type: "text" },
      { id: "body", type: "richtext" },
    ],
  }),
};
```

```ts
// src/lib/genko.ts
import { createGenko } from "@genko-me/sdk";
import { apis } from "./schema";

const apiKey = import.meta.env.GENKO_API_KEY;
if (!apiKey) throw new Error("GENKO_API_KEY is required");
export const client = createGenko({
  workspace: "your-workspace",
  apiKey,
  apis,
}).apis;
```

この例はSSG専用で、秘密値をビルド時に読みます。後からプレビューページをサーバーで動かす場合は、アダプターが推奨する実行時の秘密値の読み方へ切り替えてください。Nodeサーバーでの例は[Astro実践ガイド](https://docs.genko.me/frameworks/astro/tutorial)にあります。

## Astroで記事一覧を静的生成する

```astro
---
// src/pages/blog/index.astro
import { client } from "../../lib/genko";
const posts = await client.blog.getAll({ orders: "-publishedAt" });
---
<html lang="ja">
  <head><title>Blog</title></head>
  <body>
    <h1>Blog</h1>
    <ul>
      {posts.map(post => (
        <li><a href={`/blog/${post.id}`}>{post.title}</a></li>
      ))}
    </ul>
    {posts.length === 0 && <p>No published posts.</p>}
  </body>
</html>
```

`getAll()`はSDK内でページを進めて取得します。`list()`の最初の結果だけを使い、記事が増えると一覧から漏れる実装を避けられます。一方、大量の記事をすべて一覧へ出すとHTMLも長くなるため、本番では必要に応じて一覧を複数ページに分けます。

一覧を`fields`で絞る場合も`id`は常に返るため、リンクに使えます。ただし詳細用の全件取得まで本文を省略すると、次の`props`に本文が入りません。

## getStaticPathsで記事詳細ページを作る

```astro
---
// src/pages/blog/[id].astro
import { client } from "../../lib/genko";

export async function getStaticPaths() {
  const posts = await client.blog.getAll();
  return posts.map(post => ({
    params: { id: post.id },
    props: { post },
  }));
}
const { post } = Astro.props;
---
<html lang="ja">
  <head><title>{post.title}</title></head>
  <body>
    <article>
      <h1>{post.title}</h1>
      <div set:html={post.body} />
    </article>
  </body>
</html>
```

`getStaticPaths()`が返したIDだけが生成対象です。記事は`props`で渡しているので、同じ記事を`get()`で取り直す必要はありません。新規記事も削除した記事も、再ビルドした成果物を配信して初めてサイトへ反映されます。

genkoのリッチテキストは保存時にサニタイズされたHTMLとして配信されるため、その`body`を表示します。`set:html`は任意の入力を安全にする機能ではありません。別の外部HTMLを混ぜる場合は、その入力に対する処理を別に設計します。

## Astroのブログ画像を軽くするには？

まずCMSのリッチテキスト内の画像をそのまま表示し、レイアウトで本文画像の最大幅を制限します。アイキャッチを別フィールドにする場合は、実際の画像フィールドをスキーマと型に追加してから、そのURLを使います。

```ts
import { buildImageUrl } from "@genko-me/sdk";

const imageUrl = buildImageUrl(
  "https://media.genko.me/your-image.jpg",
  { w: 960, format: "webp", fit: "inside" },
);
```

`your-image.jpg`はCMSにアップロードした実在のメディアURLへ置き換えます。これはURLを組み立てる例で、画像は生成しません。`buildImageUrl()`の結果を`img`の`src`に使い、実際の表示サイズに合わせて幅・高さと代替テキストを設定します。アイキャッチのURL変換だけで本文内の画像すべてが変わるわけではありません。

## WebhookでAstroを再ビルドする手順

1. ホスティング先のビルド起動方法を確認します。
2. 署名付きWebhookを検証するHTTPSの受信処理を用意します。
3. CMSの`blog` APIに受信URLとシークレットを登録します。
4. 受信処理からビルドをキューへ入れ、受信自体にはすぐ応答します。
5. 公開・更新・削除・公開終了を試し、配信ページと一覧の両方を確認します。

genkoの通知はJSONのPOSTです。任意のホスティングサービスのビルドフックへ、そのまま互換接続できるとは限りません。受信処理で`verifyGenkoSignature()`を使う方法は[Webhookガイド](https://docs.genko.me/guides/webhooks)にあります。

手動公開は`entry.created`や`entry.updated`で届くため、`entry.published`だけを対象にすると更新を取りこぼします。公開から下書きへの変更や削除でも古い静的ページを除去する必要があります。通知失敗時の手動再ビルドを用意し、連続保存ではビルドをまとめると運用しやすくなります。

## 静的ブログで下書きプレビューはできる？

静的HTMLはリクエストごとの`draftKey`で本文を差し替えられません。公開記事はSSGのままにし、別のプレビュールートだけを対応アダプターでリクエスト時レンダリングする方法があります。

プレビュー側では保存済みの下書きを取得し、共有キャッシュを避け、検索対象外のメタ情報を付けます。公開ページのビルドに下書きを混ぜないでください。具体的な構成は[Astro実践ガイド](https://docs.genko.me/frameworks/astro/tutorial)を参照してください。

## 公開前に確認すること

`npm run build`と`npm run preview`で、一覧から全記事へ移動できるかを確認します。日本語URLの扱い、タイトル、canonical、記事削除後の404も対象です。本文の取得に失敗したビルドを成功扱いせず、直前の正常な成果物を維持できる運用にします。

更新反映の設計は[静的サイトのお知らせとCMS](https://blog.genko.me/static-site-news-cms)、比較は[Nuxt版](https://blog.genko.me/nuxt-headless-cms)と[SvelteKit版](https://blog.genko.me/sveltekit-headless-cms)も参照できます。

## よくある質問

### AstroのブログにヘッドレスCMSは必須ですか？

必須ではありません。開発者がGitで管理できるならMarkdownも候補になり、ブラウザで編集したい人が参加するならCMSを検討できます。

### AstroでCMSの記事が更新されないのはなぜですか？

SSGではビルドした時点のHTMLを配ります。CMSの保存後にビルドとデプロイが成功したか、古い成果物やCDNキャッシュを見ていないかを確認します。

### getAllとlistはどう使い分けますか？

全詳細ページの生成対象を集めるときは`getAll()`、表示件数やページを制御する一覧には`list()`が使えます。`list()`の最初のページだけで全記事を生成しないようにします。

### APIキーは完成したAstroサイトに必要ですか？

このSSG構成ではビルド時だけに必要です。リクエスト時に記事を取得するルートを追加した場合は、そのサーバーにも秘密値を設定します。

## 出典

- [genko Astro導入](https://docs.genko.me/frameworks/astro/getting-started)
- [genko Astro実践・プレビュー](https://docs.genko.me/frameworks/astro/tutorial)
- [genko SDKのデータ取得](https://docs.genko.me/sdk/fetching)
- [genko Webhook](https://docs.genko.me/guides/webhooks)
- [Astro公式：Routing](https://docs.astro.build/en/guides/routing/)
- [Astro公式：Content collections](https://docs.astro.build/en/guides/content-collections/)

確認日：2026年10月20日。
