みなさん、こんにちは。
近年、フロントエンドとCMSを分離する「ヘッドレスCMS」構成が注目されていますが、多くは有料サービスで、オープンソースの選択肢は限られています。
弊社が利用している baserCMS は、オープンソースでありながらヘッドレスCMSとしても活用でき、一般的なレンタルサーバーのホスティング費用だけで手軽に導入できる点が大きな魅力です。
さらに、baserCMSの公式JavaScript SDKである @ecatchup/basercms-js-sdk を使用することで、非常に簡単にAPI連携が実装できます。
今回は、Webフレームワーク「Astro」と「baserCMS」を組み合わせて、ヘッドレスCMS構成のブログを作成する方法をご紹介します。
Astro の既存のブログテンプレートをベースとして、SDK経由でbaserCMSのブログ記事を読み込んでいます。
前提条件
- ホスティング等のサーバーにbaserCMS 5系 がインストールされており、稼働していること。
- ローカル環境にNode.js がインストールされていること。
0. baserCMSのAPI有効化
Astroからデータを取得するために、baserCMSのAPIを有効化する必要があります。
baserCMSのルートディレクトリにある config/.env ファイルを編集します。
# File: config/.env
# コアが提供する Web API を利用するかどうか
export USE_CORE_API="true"
この設定を行うことで、外部からAPI経由でコンテンツにアクセスできるようになります。
1. Astroプロジェクトの作成
まず、Astroのブログテンプレートを使用してプロジェクトを作成します。
npm create astro@latest -- --template blog
プロジェクトのディレクトリに移動し、開発サーバーを起動して動作確認を行います。
cd
npm run dev
開発用のサーバーが立ち上がりますので、ブラウザで開くと次のような画面が表示されます。2. SDKのインストール
baserCMSとの通信を行うための公式SDKをインストールします。
npm install @ecatchup/basercms-js-sdk
3. API接続設定
接続先のbaserCMSのAPIエンドポイントを設定します。
src/consts.ts に定数を定義します。
// File: src/consts.ts
// ...既存のコード...
export const API_BASE_URL = 'https://your-basercms-site.com/';
※ URLはご自身のbaserCMS環境に合わせて変更してください。
4. APIクライアントの初期化
SDKの `ApiClient` を初期化するファイルを `src/lib/api.ts` として作成します。
これにより、各ページでAPIクライアントを都度設定する必要がなくなります。
// File: src/lib/api.ts
import { ApiClient } from "@ecatchup/basercms-js-sdk";
import { API_BASE_URL } from '../consts';
export const apiClient = new ApiClient({
baseUrl: API_BASE_URL
});
5. 記事一覧ページの作成
ブログのトップページでbaserCMSから記事一覧を取得して表示します。
src/pages/blog/index.astro を編集します。
getBlogPosts メソッドを使用することで、簡単に記事データの配列を取得できます。
// File: src/pages/blog/index.astro
---
import BaseHead from '../../components/BaseHead.astro';
import Header from '../../components/Header.astro';
import Footer from '../../components/Footer.astro';
import FormattedDate from '../../components/FormattedDate.astro';
import { SITE_TITLE, SITE_DESCRIPTION } from '../../consts';
// SDKとクライアントのインポート
import { getBlogPosts } from "@ecatchup/basercms-js-sdk";
import type { BlogPost } from "@ecatchup/basercms-js-sdk";
import { apiClient } from '../../lib/api';
// 記事一覧を取得
const posts = await getBlogPosts(apiClient);
---
<!doctype html>
<html lang="ja">
<head>
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
<style>
/* スタイルは省略 (Astroブログテンプレートのものを利用) */
</style>
</head>
<body>
<Header />
<main>
<section>
<ul>
{
posts.map((post: BlogPost) => (
<li>
<a href={`/blog/${post.id}/`}>
<!-- アイキャッチ画像があれば表示 -->
{post.eye_catch && <img width={720} height={360} src={post.eye_catch} alt="" />}
<h4 class="title">{post.title}</h4>
<p class="date">
<FormattedDate date={new Date(post.posted)} />
</p>
</a>
</li>
))
}
</ul>
</section>
</main>
<Footer />
</body>
</html>
6. 記事詳細ページの作成
個別の記事ページを作成します。
src/pages/blog/[...slug].astro というファイル名で作成します。
AstroのSSG(静的サイト生成)機能を利用するため、getStaticPaths で全ての記事データを取得し、props として各ページに渡します。
File: src/pages/blog/[...slug].astro
---
import { getBlogPosts } from "@ecatchup/basercms-js-sdk";
import type { BlogPost } from "@ecatchup/basercms-js-sdk";
import Layout from '../../layouts/BlogPost.astro';
import { apiClient } from '../../lib/api';
// ビルド時に全ての記事パスとデータを生成
export async function getStaticPaths() {
const posts = await getBlogPosts(apiClient);
return posts.map((post: BlogPost) => ({
params: { slug: post.id.toString() },
props: post,
}));
}
const { slug } = Astro.params;
const post = Astro.props as BlogPost;
// typeガードにより、postが存在しない場合のエラー処理
if (!post) {
throw new Error(`Post not found: ${slug}`);
}
// レイアウトコンポーネントが期待するプロパティ名にマッピング
const article = {
...post,
heroImage: post.eye_catch,
pubDate: new Date(post.posted),
updatedDate: new Date(post.modified)
}
---
<Layout {...article}>
<!-- 本文 -->
<div set:html={post.content}></div>
<!-- 詳細(追記) -->
<div set:html={post.detail}></div>
</Layout>
実装のポイント
- [...slug].astro: 動的ルーティングを使用し、URLパラメータとして id を受け取ります。
- getStaticPaths: 全記事を事前に取得しておくことで、ビルド時に静的ファイルを生成します。id は文字列である必要があるため、post.id.toString() としています。
- データマッピング: Astroのテンプレートで使用されている BlogPost.astro レイアウトに合わせるため、eye_catch を heroImage に、日付文字列を Date オブジェクトに変換して渡しています。
- HTMLのレンダリング: CMSから取得したHTML文字列を安全に表示するため、set:html ディレクティブを使用します。
これで、npm run build を実行すれば、baserCMS上の記事データをもとに高速な静的サイトが構築されます。
まとめ
basercms-js-sdk を利用することで、非常に少ないコード量でbaserCMSとAstroを連携させることができました。
AstroのパフォーマンスとbaserCMSの使い勝手を組み合わせることで、運用しやすく高速なブログサイトを構築できます。
ぜひ試してみてください!