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

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

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

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

構成

向いている運用

更新の反映

ローカルMarkdownとContent Collections

Gitで記事をレビューしたい

リポジトリ更新後のビルド

CMSとSDKで静的生成

編集者がブラウザで記事を書く

CMS更新後の再ビルド

CMSとリクエスト時レンダリング

公開直後に取得する必要がある

サーバー取得とキャッシュ方針による

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

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

  1. 既存のAstroプロジェクトを用意します。

  2. genko.meでワークスペースとリスト形式のAPI blogを作ります。

  3. titleをテキスト、bodyをリッチテキストとして登録し、記事を保存して公開します。

  4. blogの読み取りを許可したAPIキーを発行し、ビルド環境にGENKO_API_KEYを設定します。

CMS側の操作はクイックスタートを参照してください。サンプルのyour-workspaceは実際のワークスペースIDへ置き換えます。

bash
npm install @genko-me/sdk

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

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

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

ts
// src/lib/schema.tsimport { 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.tsimport { 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実践ガイドにあります。

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

astro
---// src/pages/blog/index.astroimport { 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].astroimport { 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ガイドにあります。

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

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

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

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

公開前に確認すること

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

更新反映の設計は静的サイトのお知らせとCMS、比較はNuxt版とSvelteKit版も参照できます。

よくある質問

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

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

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

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

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

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

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

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

出典

確認日:2026年10月20日。