Headless WordPress with Next.js Template> wp-headless-template
WordPressが持つ機能はそのままにヘッドレスCMSとして使い、Next.jsの開発体験も維持する。ハイパフォーマンスかつ拡張性のあるWebサイトを制作するためのテンプレート。
目次
Headless WordPress with Next.js Template
WordPressが持つ機能はそのままにヘッドレスCMSとして使い、Next.jsの開発体験も維持する。ハイパフォーマンスかつ拡張性のあるWebサイトを制作するためのテンプレート。
🔗 Demo Site: wp-headless-templateHeadless WordPress with Next.js Templatehttps://wp-headless-template.coppie.app
Features
WordPressの機能はそのまま
WordPressをヘッドレスCMSとして利用する際、実装工数などからWordPressが搭載している機能が失われがちですが、本テンプレートではWordPressの機能を維持することを意識して制作しています。
- ✅ 投稿のプレビュー
- ✅ パスワード付きの投稿
- ✅ カスタム投稿タイプ
- ✅ カスタムフィールド
- ✅ タグとカテゴリーによる絞り込み検索
- 🏃 テキストによる検索(WIP)
- 🏃 予約投稿(WIP)
- 💭 リクエストに応じて他にも
このリスト内の一部機能に対応するために独自のWordPressプラグインを同梱しています。
📦 add-custom-post-type
カスタム投稿タイプおよび、カスタムフィールドをコード上で簡単に追加するためプラグイン
このユースケースに対して一般的に用いられるプラグインは、管理画面上からGUIでフィールド等を定義し、その定義はWordPressのデータベースに保存されますが、データベースでの管理ではなく、コードベースでの管理がしたかったため作成したプラグインです。
以下のフィールドタイプをサポートしています。
'text'|'number'|'select'|'checkbox'|'image'|'url'|'loop'
📦 change-post-preview
投稿のプレビューリンクを変更して、フロントエンドのテーマで安全にプレビューするためのプラグイン
WordPressのプレビューを特定のURLにリダイレクトすることで、プレビュー機能をWordPress以外のフロントエンドで利用できるようにします。Next.js側ではDraft Modeを用いてプレビューの体験を向上します。
また、アプリケーションパスワードの万が一の漏洩に備えて、このプラグインを用いて必要最小限の権限を持つロールを作り、そのロールが指定されたユーザーのアプリケーションパスワードをプレビュー用に使用することでリスクを低減させます。
Docker DesktopとNode.jsだけで完了する環境構築
wp-envを最大限活用することで、柔軟なWordPressローカル環境を構築しました。
wp-envを動作させるのに必要なDocker DesktopとNode.jsの2つがインストール済みなら、わずかなコマンドを実行するだけですぐにWordPressとNext.jsの開発環境を構築できます。
Next.js App Routerの採用
React Server Components(RSC)を基準としたApp Routerを採用し、ほとんどのコンポーネントをサーバーコンポーネントとして実装しました。
複数のレンダリングパターン(Static Rendering / Dynamic Rendering / Streaming / Client-side rendering
)を適切に使い分け、ハイパフォーマンスかつ機能的なWebサイトを開発するための良い出発点となるように設計しています。
このテンプレートで、App Routerの初見では分かりづらいSuspense
, 'use client'
, searchParams
, cookies
などについて実例をもとに理解を深めることができます。
より良い開発体験の追求
RSCの副次的な恩恵として、コンポーネント単位でサーバーサイドのfetchリクエストを行えるようになりました。
今まではWordPressをヘッドレスCMSとして使う際に、WordPress REST APIをGraphQLに変換するプラグインを導入する人も多かったと思いますが、RSCを使う事で標準のREST APIを効率的に使えるようになります。
また、TypeScriptからWordPress REST APIを利用する際に適切な型を扱えるようにwp-typesを導入し、より具体的な型を受け取れるように拡張しました。
さらに、よく使うWordPress REST APIのエンドポイントへのfetchをラップした以下のような関数を用意しており、これらの関数を使うことで何もコードを書かずに型の恩恵を受けることができます。もちろん、fetchWp()
を使って新しいリクエストを作るのも簡単です。
// ~/helpers/api
import { fetchWp } from '~/helpers/client'
import type { FetchWpParams } from '~/helpers/client'
import type { Post } from '~/types/wordpress'
export const getTotalPagesOfPosts = async (params?: FetchWpParams) => {
const { headers } = await fetchWp<Post[]>({
path: '/posts',
params: {
context: 'embed',
...params,
},
})
const totalPages = Number(headers.get('x-wp-totalpages'))
if (Number.isNaN(totalPages)) return 0
return totalPages
}
その他にもテストにVitest、コードフォーマットにPrettier、コード検証にESLint、スタイリングにTailwind CSS、CI/CDにGitHub Actionsを採用しており、初期設定を行う必要なくすぐにこれらのツールを活用できます。
Getting Started
事前準備が必要なツール
テンプレートの利用方法
git clone
コマンド等でリポジトリをクローンnpm install
を実行して依存関係をインストールnpm run wp:init
を実行してWordPressを初期化- WordPress: http://localhost:8888
wp:init
の実行前にDocker Desktopが起動している必要がありますwp:init
後は、npx wp-env start/stop
でDockerコンテナを起動/停止できます(その他のコマンドはwp-env Command Referenceを参照)
.env.sample
をコピーして.env
を作成npm run dev
を実行してNext.jsを起動- Next.js: http://localhost:3000
# 管理者ユーザーの情報
login: admin
password: password
email: [email protected]
(任意)WordPressの投稿プレビューをフロントエンドで行う
.wp-env.override.example.json
をコピーして.wp-env.override.json
を作成- 任意のトークンジェネレーターでトークンを生成して
.env -> DRAFT_MODE_TOKEN
と.wp-env.override.json -> WP_DRAFT_MODE_TOKEN
に追記- 開発環境に限ってはどんな文字列でも構わないので、お好きな文字列を設定しても問題ありません
- ここで設定したトークンは投稿のプレビューを行う際にリダイレクトするNext.jsのRoute Handlerで、トークンが同じ値であるか検証するために使用されます
npm run wp:preview
を実行して、出力されるアプリケーションパスワードを.env
のWORDPRESS_APP_PASSWORD
に追記wp:preview
はpreviewユーザーの作成/設定を実行するコマンドです。コマンドの本体はscripts/wp-prepare-preview.sh
に記述されています
# プレビューユーザーの情報
login: preview
password: password
email: [email protected]
(任意)お問い合わせフォーム等でのメール送信を行う
- Resendに登録してAPIキーを取得
.env
のRESEND_API_KEY
を設定- 必要に応じて
RESEND_FROM_EMAIL
にメールアドレスを設定すると、そのメールアドレスでのメール送信が行えます - Resendの管理画面で送信内容が確認できますが、Resendでのドメイン設定等が完了していないと実際には届きません
- 必要に応じて
(任意)WordPressテーマ/プラグインの開発を行う
- Composerをインストール
- cdコマンドで
wordpress
ディレクトリに移動 composer install
を実行して依存関係をインストール
Note
[!NOTE] 現状、このプロセスはPHP CodeSnifferでPHPのリンティングおよびフォーマッティングを行うためだけに用意しているため、WordPressテーマ/プラグインの開発自体はこのプロセスを実施しなくても可能です。
また、
.phpcs.xml
に記載のPHP CodeSnifferの設定はWordPressのコーディング規約に準拠しています。この規約では、グローバルな関数や変数には独自の接頭辞を付けることが求められています。
デフォルトではcustom
という接頭辞を設定していますが、必要に応じてWordPress.NamingConventions.PrefixAllGlobals
のprefixesに追加してください。
Development Notes
Node.js/npmのバージョン管理
.node-version
に記載のバージョンのNode.jsを使用します。
これはnodenvを使用する前提で配置されたファイルです。そのため、nvmやvoltaなどを使用する場合は、記載のバージョンを別途.nvmrc
やpackage.json
に追加してください。
また、npmのバージョンはCorepackで管理します。CorepackはNode.js v14.19.0以降に同梱されています。
Corepackでnpmを管理下に置くにはcorepack enable npm
を実行します。
ただ、Corepackはまだ実験的なツールであり、npmのバージョン差異による影響も限定的であるため、特に本プロジェクトにおいて導入は必須ではありません。
GitHub Actionsのローカル実行
./github/workflows
にGitHub Actionsの設定ファイルを配置しています。
ローカルでのジョブの実行確認にはactを使用できます。
また、ジョブ内で使用する環境変数は.secrets
に記載することでactから参照できるようになります。
wp:initでインポートされるWordPressデータの変更
wp:init
でインポートされるデータはtheme-test-data-jaを利用しています。
このデータはWP-CLIのwp db export
コマンドでwordpress/migrations/seed.sql
にエクスポートされています。
wp:init
はこのSQLファイルをインポートして初期データを読み込んでいます。そのため、各々で都合の良いWordPressデータをwp db export
コマンドでエクスポートすることで、wp:init
でのインポートデータを変更することができます。
また、WP-CLIのwp db import/export
コマンドをこの用途に使い易いように、npm scriptsにwp:import/wp:export
コマンドを用意しています。このラッパーコマンドではexportが不要と思われるテーブルは除外しているため、必要に応じて編集してください。
Production Deployment
wp-headless-templateを本番環境で使用する際は、以下の項目を確認してください。
Next.jsのホスティング
結論から言うと、Next.jsはVercelでホスティングするのが最も考えることも少なく、確実で、容易です。
Next.jsのセルフホスティングについては幾つかの方法がありますが、その際にはRevalidationについて考慮する必要があります。
Pages RouterではISR(Incremental Static Regeneration)と呼ばれているデータ再検証の仕組みは、Pages Routerにおいては静的なWebページ生成に留まっていましたが、App Routerではより広範にキャッシュされたデータを再検証する機能として存在しています。
そのため、App RouterにおけるRevalidationはWebページの静的レンダリングだけでなく、サーバーサイドでの動的レンダリングにも適用される仕組みとなっており、Next.jsのパフォーマンスを最大限発揮するためにはこの機能を適切に活用する必要があります。
現時点ではVercel以外でRevalidationを確実に動作させることは難しいと思いますが、SSR(Server Side Rendering)に限ってWebサイトを構築する場合は、Cloudflare PagesのEdgeランタイムで動かしてパフォーマンスをカバー出来るかもしれません。
Warning
[!WARNING] このテンプレートがCloudflare Pagesで動作するかは未検証です。
ただ、Cloudflare側でレンダリング結果をキャッシュすることで、Time-based Revalidationのような仕組みは実現できるのではないかと考えています。 同様に、On-demand RevalidationのようなこともCMSのデータ更新をフックにしてCloudflareのキャッシュを削除することで実現できるかもしれませんが、こちらも未検証なので保証はできません。
また、このテンプレートは
/news/[id]
ページに関しては静的生成して最大のパフォーマンスを発揮できるようにしていますが、Cloudflare Pagesで動かす等の事情によりSSRに移行する場合は、Preloading Dataパターンを参考にすると高いパフォーマンスを維持できるかもしれません。
あるいは、Static Exportで静的にWebサイトを出力する場合であれば、HTML/CSS/JS等の静的アセットを提供できる任意のWebサーバーにホスティングできます。
Webサイトの性質によっては静的な出力で十分な場合もありそうですが、この場合はRevalidationもSSRも出来ません。
そのため、データの更新を反映させるには、CMSのデータ更新をトリガーに静的アセットを再生成する必要があります。 また、SSRが出来ないと記事のプレビューも出来ないため、プレビューについてはクライアントコンポーネントとして実装し直さないといけません。
詳しい情報は以下のドキュメントを参照してください。
Building Your Application: Deploying | Next.jsLearn how to deploy your Next.js app to production, either managed or self-hosted.https://nextjs.org/docs/app/building-your-application/deploying
WordPressのホスティング
WordPressのホスティングは多様な選択肢がありますが、デモサイトではさくらのレンタルサーバーを使用しています。
さくらのレンタルサーバーを使用している理由は、何より安価であることと、WP-CLI
がデフォルトで使えるくらいです。
本テンプレートを動かすにあたってWordPressに対しての特別な要件はないので、お好きなホスティングサービスをご利用ください。
また、任意ではありますが、SSHでのファイル操作が可能な場合、定義済みのGitHub Actionsのジョブ(.github/workflows
)によって特定のGitHubのアクションをトリガーに自動的にWordPress関連のファイルを更新することができます。
SSHの要件にくわえて、複数のWordPressのホスティングが可能な場合、ステージング環境用のWordPressを用意することで、GitHubでのプルリクエスト時にステージング環境にデプロイすることも可能です。
環境変数と定数の設定
開発環境では.env
と.wp-env.override.json
にて環境変数と定数を設定していますが、本番環境ではデプロイする環境に合わせて適切な方法で設定してください。
.wp-env.override.json
のconfigに指定する定数は、wp-config.php
にてdefine()
で定義するのと同じ扱いとなっています。以下のようにwp-config.php
に追記することで定数を設定できます。
define( 'WP_FRONTEND_HOST', 'wp-headless-template.coppie.app' );
define( 'WP_DRAFT_MODE_TOKEN', 'hs32rTl5cpg3V4sPXSmQ6U8MEIssmB2V6YOszUhS' );
必須ではないですが、本テンプレートでCI/CDとして利用しているGitHub Actionsにもシークレットを設定すると、WordPress関連のファイルをSSHで自動的にデプロイできるようになります。
.github/workflows/deploy-production.yml
と.github/workflows/deploy-staging.yml
にて、以下のシークレットを使用しています。
SERVER_SSH_PRIVATE_KEY: # SSH秘密鍵
SERVER_HOST: # SSHホスト
SERVER_USER: # SSHユーザー
SERVER_PORT: # SSHポート
SERVER_PATH: # 本番環境のWordPressディレクトリまでのパス (例: `/home/coppie/www/headless-wp`)
SERVER_STAGING_PATH: # ステージング環境のWordPressディレクトリまでのパス (例: `/home/coppie/www/headless-wp-staging`)
FAQ & Troubleshooting
wp-block-template / wp-headless-templateのどちらを使えば良い?
それぞれのテンプレートの違いを把握することが選択の判断材料になると思います。
細かい違いは色々とありますが、明らかな違いとしては以下のような項目が挙げられそうです。
wp-block-template
- WordPress管理画面からノーコードツールのような感覚でWebサイトの内容を変更できる
- ホスティングのために用意するサーバーが1つで済むため、小〜中規模であればランニングコストが抑えられる
- WordPressテーマ開発者にとっては、テーマ開発の知識がそのまま活かせる
- Webサイトの内容次第では、HTMLとCSSを書くだけでWebサイトを構築できる
wp-headless-template
- 開発の柔軟性・拡張性が高い
- 高速なページ遷移やインタラクティブなUIを実装しやすい
- 大規模なアクセスを効率よく処理できるため、コスト面を含めスケーラビリティが高い
- 実装方法次第では、セキュリティリスクを大きく低減できる
大雑把に言えば、小〜中規模のアクセスが見込まれるWebサイトで、ランニングコストもあまりかけられないような場合においては、wp-block-templateは手離れしやすいWebサイトを開発できる良い選択となりそうです。
Tip
[!TIP] 上記のリストには含めていませんが、テンプレート自体はサードパーティのプラグインをWP Multibyte Patch以外使用しておらず、不具合・脆弱性対応なども最小限に抑えられると思います。
反対に、wp-headless-templateは大規模なアクセスが見込まれるケースや、セキュリティが重視されるケース、Webサイトの拡張性を最大化したいケースなどに適した選択です。 前者2つのケースにおいてはwp-block-templateでも対応可能ですが、最後の「Webサイトの拡張性を最大化したいケース」への対応はwp-headless-templateでないと困難を極めます。
Tip
[!TIP] 具体的には、複数のデータソースやWebアプリケーション的な機能を持つWebサイトの開発が難しくなるはずです。
例えば、Shopify Storefront APIを使ってヘッドレスにECサイトを構築してその中にWordPressを使ったブログも含めたり、ユーザーアカウントのサブスクリプション状態に応じて記事の閲覧権を付与したり、そういった要件がこれに該当します。
また、個々の開発者のスキルセットに依存するとは思いますが、WordPressテーマ/プラグインの開発しか経験がない場合はwp-headless-templateを使いこなすまでの道のりは険しく、一方で、普段からNext.jsなどでWebアプリケーションを開発しているような方にとってはwp-block-templateは煩わしさを感じる部分も多いと思います。
とはいえ、それぞれのテンプレートで採られている手段を1から開発するのに比べれば、相当な時間短縮が図れるのは間違いないので、Webサイトの要件に応じて各テンプレートを選び、それぞれの開発手法にチャレンジしてみるのも良いのではないかと思います。困ったことがあれば気軽にご相談ください。
npm run wp:init
でWordPressが起動しない
まず、Docker Desktopが起動しているか確認してみてください。
Docker Desktopが起動しているにも関わらずnpm run wp:init
でWordPressが起動しない場合、wp-env
を別のプロジェクトで使っている可能性があります。
別のプロジェクトで使っている場合は、そのプロジェクト内でnpx wp-env stop
を実行してから、再度npm run wp:init
を実行してください。
それでも解決しない場合は、npx wp-env clean all
あるいはnpx wp-env destroy
を実行してから再度お試しください。
WordPressから投稿のプレビューができない
まず、READMEの 「(任意)WordPressの投稿プレビューをフロントエンドで行う」 が正しく実施できているか確認してみてください。
実施済みであるにも関わらずWordPressから投稿のプレビューができない場合、一度ログアウトしてから再度ログインすると治ることを確認しています。
WordPressのREST APIエンドポイントが機能しない
まれに/wp-json/wp/v2/posts
などのREST APIエンドポイントが機能しないことがあります。
npm run wp:restart
でWordPressの再起動を行なったり、管理画面の「設定->パーマリンク」の「変更を保存」ボタンを押すことで治ることを確認しています。
その他
その他、不具合や疑問点があれば、とにて気軽にご相談ください。
License
本テンプレートのライセンスはApache License 2.0に基づきます。詳細はLICENSEを参照してください。
また、WordPressのGPL(GNU General Public License)によって認められる権利を除き、NOTICEに記載のように、本テンプレート内のソースコードを再配布することはできません。