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でブログを作る前に用意するもの
既存のAstroプロジェクトを用意します。
genko.meでワークスペースとリスト形式のAPI
blogを作ります。titleをテキスト、bodyをリッチテキストとして登録し、記事を保存して公開します。blogの読み取りを許可したAPIキーを発行し、ビルド環境にGENKO_API_KEYを設定します。
CMS側の操作はクイックスタートを参照してください。サンプルのyour-workspaceは実際のワークスペースIDへ置き換えます。
npm install @genko-me/sdkAPIキーをPUBLIC_付きの変数へ入れたり、ブラウザで動くスクリプトへ渡したりしないでください。静的生成ならキーが必要なのはビルド環境で、完成したHTMLを配るサーバーにキーを持たせる必要はありません。
SDKで記事の型と接続先を定義する
次の2ファイルを作ります。型だけを書いても管理画面にフィールドは作られません。CMS側のフィールドID・種類を同じにしてから接続します。
// 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" }, ], }),};// 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で記事一覧を静的生成する
---// 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で記事詳細ページを作る
---// 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を使います。
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を再ビルドする手順
ホスティング先のビルド起動方法を確認します。
署名付きWebhookを検証するHTTPSの受信処理を用意します。
CMSの
blogAPIに受信URLとシークレットを登録します。受信処理からビルドをキューへ入れ、受信自体にはすぐ応答します。
公開・更新・削除・公開終了を試し、配信ページと一覧の両方を確認します。
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日。
