なんかAstro.js関連の記事続いてしまってます。
WordPressをヘッドレスCMSとして利用して、Astro.jsでフロントエンドを作るというお話です。

要するに、WordPressはあくまで管理画面から記事を投稿する為に利用し、その投稿したものはWordPressのREST APIを利用してAstro.jsで作成するフロントエンド側で表示するようにします。

 

WordPressヘッドレスCMS化の手順

1. Astro.jsをインストール

Astro.jsをインストールして、localhost:3000(環境によって違うかもですが)でブラウザ上で確認できるようになっていて、ページやレイアウトやコンポーネントはある程度形にしておくといいですね。
今回の記事はTypeScriptを使っています。

Astro.jsのインストール方法等は記事にしてあるので、下記を見てください。

https://blog.tesoro-crea.biz/web/414/

 

2. WordPressをインストール

まずはWordPressを普通にインストールして、管理画面を使えるようにします。
テーマはなんでもいいと思います。どれにしても機能的なものやプラグイン等は使えません。

 

3. WordPressで記事を投稿する

適当に記事を投稿しておいてください。

 

4. Astro.js側で受け取るコードを書く

.envにREST APIのURLを追加する

プロジェクトルートの.envに下記を追加します。

PUBLIC_API_URL = https://◯◯◯◯/wp-json/wp/v2/

◯◯◯◯の部分はドメイン名に置き換えてください。

 

投稿一覧を表示する

Newsを投稿した場合の例です。
News一覧を表示したいページを下記のようにします。

src/pages/news.astro

---
import Layouts from "../layouts/Layouts.astro";

let res = await fetch(`${import.meta.env.PUBLIC_API_URL}posts?_embed`);
let posts = await res.json();
function formatDate(dateString: string): string {
  const date=newDate(dateString);
  const year=date.getFullYear();
  const month= (date.getMonth() + 1).toString().padStart(2, "0");
  const day=date.getDate().toString().padStart(2, "0");
  return `${year}.${month}.${day}`;
}
---

<Layouts title="ニュース一覧">
  <section>
    <h1>ニュース一覧</h1>
    {
      posts.map((post: any) => (
        <article>
          <a href={`/news/${post.slug}/`}>
            <div class="eyecatch">
              <img src={post._embedded["wp:featuredmedia"]["0"].media_details.sizes.large.source_url} alt={post.title.rendered} />
            </div>
            <div class="title">{post.title.rendered}</div>
            <div class="date">{formatDate(post.date)}</div>
          </a>
        </article>
      ))
    }
  </section>
</Layouts>

2行目、

import Layouts from "../layouts/Layouts.astro";

は環境に合わせて変更してください。

4行目、

let res = await fetch(`${import.meta.env.PUBLIC_API_URL}posts?_embed`);

?_embed はアイキャッチ画像を取得するために必要なのでつけています。
アイキャッチ画像が不要な場合は削除してください。

6〜12行目、

function formatDate(dateString: string): string{
  const date = newDate(dateString);
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, "0");
  const day = date.getDate().toString().padStart(2, "0");
  return `${year}.${month}.${day}`;
}

これは、日付データも取得するので、フォーマットするようにします。

21行目、

<a href={`/news/${post.slug}/`}>

post.idの部分はお好みで変更してください。
スラッグ名がよければ、post.slugになります。

23行目、

<img src={post._embedded["wp:featuredmedia"]["0"].media_details.sizes.large.source_url} alt={post.title.rendered} />

アイキャッチ画像を取得しています。取得する画像サイズはlargeになってますが、fullやmedium等デザインに合うサイズで変更してください。

25〜26行目、

<div class="title">{post.title.rendered}</div>
<div class="date">{formatDate(post.date)}</div>

タイトルと投稿日を取得しています。
日付の表示形式は、13行目の、

return `${year}.${month}.${day}`;

ここで変更できます。

これで投稿一覧が表示されるようになります。

 

記事ページを生成する

https://◯◯◯◯/news/[slug]/
のURLにしたいので、

src/pages/news/[slug].astro を作成し下記のようにします。

---
import Layouts from "../../layouts/Layouts.astro";

const { slug } = Astro.params;
let res = await fetch(`${import.meta.env.PUBLIC_API_URL}posts?slug=${slug}&_embed`);
let [post] = await res.json();
export async function getStaticPaths() {
  let data = await fetch(`${import.meta.env.PUBLIC_API_URL}posts`);
  let posts = await data.json();

  return posts.map((post: any) => ({
    params: { slug: post.slug },
    props: { post: post },
  }));
}
function formatDate(dateString: string): string {
  const date = newDate(dateString);
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, "0");
  const day = date.getDate().toString().padStart(2, "0");
  return `${year}.${month}.${day}`;
}
---
 
<Layouts title={post.title.rendered}>
  <article>
    <h1>{post.title.rendered}</h1>
    <div class="date">{formatDate(post.date)}</div>
    <div class="eyecatch">
      <img src={post._embedded["wp:featuredmedia"]["0"].media_details.sizes.large.source_url} />
    </div>
    <Fragment set:html={post.content.rendered} />
  </article>
</Layouts>

5行目、

let res = await fetch(`${import.meta.env.PUBLIC_API_URL}posts?slug=${slug}&_embed`);

記事ページでもアイキャッチを取得したいので、&_embedをつけています。
アイキャッチ画像が不要な場合は削除してください。build失敗します。

32行目、

<Fragment set:html={post.content.rendered} />

これで投稿した記事の詳細がHTMLで出力されます。

補足で、デフォルトだと記事が10件しか取得できないのですが、例えば50件等取得したい場合は、8行目、

let data = await fetch(`${import.meta.env.PUBLIC_API_URL}posts`);

let data = await fetch(`${import.meta.env.PUBLIC_API_URL}posts?per_page=50`);

このようにパラメータを付けてあげると取得できました。(詳しく調べてませんが、REST APIの仕様上最大100件かも??)

 

buildする

ここまでできたら、

npm run build

でbuildします。

すると、下記のようにHTMLが生成されます。

あとは、スタイルを調整して完了です。

(slugで今回やりましたけど、idでやろうとすると、idの番号のディレクトリにはなるのですが、中身のindex.htmlが全部同じファイルになってしました。。。なぜでしょう?調べておきます。)