カルキチブログ

WordPressのヘッドレス化が簡単にできるFaust.jsについてと触ってみた感想

つい最近の話ですが、WordPressのヘッドレス化を簡単に行うことができるFaust.jsなるフレームワークが出たらしいです。

https://twitter.com/abefumito/status/1438772470215970817

JamstackとWP好きなエンジニアとしてこれは放って置けないと思ったので、軽く触ってみた感想について記事を書いてみました。

Faust.jsとは?

Faust.jsとは、WordPress(以下WPという略称を使用)のヘッドレス化をする際の便利機能や環境を提供してくれるフロントエンドフレームワークです。

ざっと触ってみた感じですが、以下のような特徴・機能を持っています。

  • WPから必要な情報を取得するための便利なフックス(データの取得や認証、プレビューなど)が使用できる。
  • 記事ページやカテゴリーページ、ページネーション処理など、サイトを構築するのに必要な機能が最初からある程度できている。
  • WPの記事をSSR・SSGを使って表示できる。

Faust.jsのフロントエンドにはNext.jsが採用されているので、コンテンツの管理にWPを使いつつ、フロント側をTypeScript・Reactというモダンな環境で構築するのが、今までよりも簡単にできるようになっています。

WPのヘッドレス化自体はFaust.jsを使用しなくても可能ですが、記事のプレビューをどうするのか問題や、WPに関する知見も必要になるので、実案件に取り入れるのはハードルが高いのが現状です。

→WP REST APIを用いて投稿データの取得を行うので、WPに関する深い理解も求められそうな印象

Faust.jsにはWPのヘッドレス化を比較的簡単に実現できる仕組みが用意されているので、WPのヘッドレス化を行う際の一つの手段として今後注目されそうです。

※まだプレリリース版なので、実戦投入はやめたほうがよさそうです。

環境構築と必要な設定について

今回はローカルで環境で実行環境を構築してみました。

作った環境はGithubにあげているので、興味がある方は見てみてください。

→WPの実験用に使っている環境なので、不要なプラグインも色々入ってます。

https://github.com/Yota-K/wp-next-js-sample

Next.js側の環境構築

まずは、以下のコマンドを実行して、プロジェクトの作成を行います。

npx create-next-app \ -e https://github.com/wpengine/faustjs/tree/canary \ --example-path examples/next/getting-started \ --use-npm \ my-app cd my-app

実行が完了したら、以下のようなファイル群が生成されます。

. ├── README.md ├── gqty.config.js ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── public │   ├── favicon.ico │   └── images │       └── headless_hero_background.jpg ├── src │   ├── client │   │   ├── index.ts │   │   └── schema.generated.ts │   ├── components │   │   ├── CTA.tsx │   │   ├── Footer.tsx │   │   ├── Header.tsx │   │   ├── Heading.tsx │   │   ├── Hero.tsx │   │   ├── Pagination.tsx │   │   ├── Posts.tsx │   │   └── index.ts │   ├── faust.config.js │   ├── pages │   │   ├── 404.tsx │   │   ├── [...pageUri].tsx │   │   ├── _app.tsx │   │   ├── _document.tsx │   │   ├── api │   │   ├── category │   │   ├── custom-page.tsx │   │   ├── index.tsx │   │   ├── posts │   │   └── preview.tsx │   └── scss │       ├── _typography.scss │       ├── _variables.scss │       ├── components │       ├── main.scss │       └── pages └── tsconfig.json

ファイルが生成されたら、cp .env.sample .envでenvのコピーを行い、npm run devで開発用のdevサーバーを立ち上げます。

こんな感じの画面が表示されたらフロント側の環境構築はほぼ完了です。

フロント側の環境構築の方法についてざっと解説しましたが、詳しい環境構築の方法が知りたい方は公式ドキュメントを確認してください。

https://faustjs.org/docs/next/getting-started

WordPress側で必要な設定

次にWP側で必要な設定について解説していきます。

Faust.jsを使用するには、「WP GraphQL」と「WP Engine Headless」というプラグインが必要になるので、インストールを行います。

WP GraphQLは管理画面上からインストールできるのですが、WP Engine Headlessは管理画面上からインストールできないので、Faust.jsの公式からインストールを行います。

以下のリンクからインストールを行うことができます。

Download the latest versionというリンクからインストール可能です。

https://faustjs.org/docs/tutorial/setup-faustjs

インストールしたプラグインの有効化を行なったら、設定にHeadlessという項目が追加されているはずなので、クリックすると以下のような画面が表示されます。

Front-end site URLには、フロントエンド側のURLを入力します。

今回はローカル環境で動作させるので、localhost:3000と入力しています。

Front-end site URLの下に表示されているSecret Keyもすぐに必要になるので、ここでコピーしておきましょう。

次にNext.js側に戻ってNext.jsのenvに環境変数の設定を行います。
環境変数には、WP環境のURLと先ほどコピーしたシークレットキー2つの情報の設定を行います。

NEXT_PUBLIC_WORDPRESS_URL=WP環境のURL WP_HEADLESS_SECRET=さっきコピーしたシークレットキー

最後に、設定のパーマリンクの設定をクリックして、パーマリンクの構造を/posts/%postname%/に変更します。

これで必要な設定は全て完了です。

設定が完了したら、http://localhost:3000/posts/hello-worldでアクセスしてみましょう。

すると、、、

添付した画像のように、WPで管理している記事がNext.jsで表示されていることが確認できるかと思います。

上記のような画面が表示されてたら初期設定は完了です!

実際に触れてみた感想

実際に触ってみた感想についてはこんな感じです。

プロジェクトの雛形を作った段階で、必要な機能や処理がある程度できている

Faust.jsを使って、Next.jsのプロジェクトを立ち上げて一番驚いたのはこの点でした。

以下の機能や処理はプロジェクトを立ち上げた段階ですでにできています。

  • プレビュー機能とプレビューコンテンツを表示するためのページ
  • 404ページ
  • 投稿ページ・固定ページを表示するためのページ
  • カテゴリーページとページネーション

シンプルなサイトだったら、ちょっと処理を追加すれば大枠はすぐ完成しそうなレベルで、必要なものが揃っています。

特にすごいと思ったのはプレビュー機能です。

WPの管理画面上から記事のプレビューを行えるのは本当に便利だなと感じました。

使用頻度の高い処理が最初から揃っているのはとてもいいですね!

便利なフックスが多い

WPの機能をReactで使用できる便利なフックスが多いのもFaust.jsの強みです。

usePostというフックスを使用すると、WPのAPIから取得した投稿をNext.js側で表示することができます。

Next.js では[param]のようにしてページ名に角括弧を使うことで動的なルーティングを作ることができます。

Faust.jsを使わないで、WPをヘッドレス化する際はどのページ名にルーティング処理を割り当てるかは自分で設定する必要がありましたが、Faust.jsだと特に何も設定しなくても動的なルーティングを使用することができます。

使用できる動的ルーティングは以下になります。

  • [postId]・・・投稿IDでページにアクセスできるようになる。
  • [postSlug]・・・管理画面上で指定したスラッグでページにアクセスできるようになる。
  • [postUri]・・・uriを使用して、ページにアクセスできるようになる。

デフォルト状態だと、posts/[postSlug]/index.tsxというルーティングがあるので、投稿ページに投稿を作成すると、posts/作成した投稿のスラッグで記事にアクセスできるようになっています。

import { getNextStaticProps, is404 } from "@faustjs/next";
import { client, Post } from "client";
import { Footer, Header, Hero } from "components";
import { GetStaticPropsContext } from "next";
import Head from "next/head";

export interface PostProps {
  post: Post | Post["preview"]["node"] | null | undefined;
}

export function PostComponent({ post }: PostProps) {
  const { useQuery } = client;
  const generalSettings = useQuery().generalSettings;

  return (
    <>
      <Header
        title={generalSettings.title}
        description={generalSettings.description}
      />

      <Head>
        <title>
          {post?.title()} - {generalSettings.title}
        </title>
      </Head>

      <Hero
        title={post?.title()}
        bgImage={post?.featuredImage?.node?.sourceUrl()}
      />

      <main className="content content-single">
        <div className="wrap">
          <div dangerouslySetInnerHTML={{ __html: post?.content() ?? "" }} />
        </div>
      </main>

      <Footer copyrightHolder={generalSettings.title} />
    </>
  );
}

export default function Page() {
  const { usePost } = client;
  const post = usePost();

  return <PostComponent post={post} />;
}

export async function getStaticProps(context: GetStaticPropsContext) {
  return getNextStaticProps(context, {
    Page,
    client,
    notFound: await is404(context, { client }),
  });
}

export function getStaticPaths() {
  return {
    paths: [],
    fallback: "blocking",
  };
}

useQuery().generalSettingsWPで設定したタイトルとデスクリプションも簡単に取得可能です。

usePostと似たフックスとしてはusePageというフックスがありますが、このフックスは固定ページの情報を取得してNext.js側で表示することができます。

このフックスでは、[pageId][pageUri]の2種類が使用可能です。

import { getNextStaticProps, is404 } from '@faustjs/next';
import { Footer, Header, Hero } from 'components';
import { GetStaticPropsContext } from 'next';
import Head from 'next/head';
import { client, Page as PageType } from 'client';

export interface PageProps {
  page: PageType | PageType['preview']['node'] | null | undefined;
}

export function PageComponent({ page }: PageProps) {
  const { useQuery } = client;
  const generalSettings = useQuery().generalSettings;

  return (
    <>
      <Header
        title={generalSettings.title}
        description={generalSettings.description}
      />

      <Head>
        <title>
          {page?.title()} - {generalSettings.title}
        </title>
      </Head>

      <Hero
        title={page?.title()}
        bgImage={page?.featuredImage?.node.sourceUrl()}
      />

      <main className="content content-single">
        <div className="wrap">
          <div dangerouslySetInnerHTML={{ __html: page?.content() ?? '' }} />
        </div>
      </main>

      <Footer copyrightHolder={generalSettings.title} />
    </>
  );
}

export default function Page() {
  const { usePage } = client;
  const page = usePage();

  return <PageComponent page={page} />;
}

export async function getStaticProps(context: GetStaticPropsContext) {
  return getNextStaticProps(context, {
    Page,
    client,
    notFound: await is404(context, { client }),
  });
}

export function getStaticPaths() {
  return {
    paths: [],
    fallback: 'blocking',
  };
}

usePostsというフックスは、複数の投稿をまとめて取得することができます。

以下のように指定すると、検索条件に一致した投稿を6件、登録した順でまとめて取得することができます。

import { getNextStaticProps } from '@faustjs/next';
import { client } from 'client';

export default function Home() {
  const { usePosts } = client;
  const posts = usePosts({
    first: 6,
  });

  return (
    <>
      <h2>Recent Posts</h2>
      <ul>
        {posts?.nodes.map((post) => (
          <li key={post.id}>{post.title()}</li>
        ))}
      </ul>
    </>
  );
}

whereを使用すると、カテゴリーごとに投稿を絞り込むこともできます。

→補完を見た感じだと、タクソノミーで投稿を絞り込むこともできそうです。

import { getNextStaticProps } from '@faustjs/next';
import { client } from 'client';

export default function Home() {
  const { usePosts } = client;
  const posts = usePosts({
    first: 6,
    where: {
      categoryName: "hoge",
    },
  });

  return (
    <>
      <h2>Recent Posts</h2>
      <ul>
        {posts?.nodes.map((post) => (
          <li key={post.id}>{post.title()}</li>
        ))}
      </ul>
    </>
  );
}

その他にも、Next.js上でWPの記事のプレビューを可能にするusePreview(何も設定しなくても投稿ページは使用可能だった)や、認証状態をチェックし認証済みだったらコンテンツを表示することができるuseAuthなどがあります。

他のフックスや機能に関して知りたい方は、公式ドキュメントを確認してみてください。

https://faustjs.org/docs/next/reference/custom-hooks

採用するにおいて課題になりそうな部分

個人的にはすごくいいなと感じたFaust.jsですが、採用するにおいて課題になりそうな点もあったので、こちらについてもまとめてみました。

導入するのに必要な技術レベルは結構高め

Faust.jsを使用してWPをヘッドレス化した場合、一般的なWPで作られたサイトと違って、実際にユーザーが見るフロント側の環境も必要になります。

フロント側の環境は、AWSだとS3、CloudFront、AWS Amplify、ホスティングサービスを使うならVercelやNetlifyなどを使用することになるかと思うのですが、これらのクラウドやホスティングサービスは専門的な知識が必要なので、エンジニア以外の人が保守運用を行うのはかなり厳しいと思います。

React、Next.js、TypeScriptなどのフロントエンドの知見に関しては言わずもがなですが、フロント環境を配信するためのホスティングサービスやサイトの規模が大きくなるとAWSなどのクラウド周りの知見も必要になりそうなので、Faust.jsを導入するには結構高めの技術レベルが求められるという点はデメリットであると言えそうです。

また、データ構造が複雑なサイトを構築しようとすると、カスタム投稿やタクソノミーをどのように分けていくかを考えたり、WP REST APIで取得できそうにないデータが出てきたりすると、独自のAPIのエンドポイントを追加したりも出てくると思うので、規模の大きい複雑なサイトを構築する場合はWPの知見も結局は求められると思います。

ここ最近のフロントエンド技術(ReactとかVueとか、TSとか)とWPのどっちもできる人は割とレア人種だと思うので、求められる技術レベルが結構高いのは導入するに際して大きな障害になると言えそうです。

受託の案件に取り入れるのは厳しそう

制作したものを作って納品することが目的の受託案件だと、作って終わるケースも多いと思います。

自社制作や自社開発だと、技術的な問題が起きてもすぐに対応できるので、問題にはならないと思いますが、何かトラブルが発生してもクライアント側ですぐに解決するのが難しい受託の案件には向いていないと思いました。

まとめ

まとめます。

  • WPをヘッドレス化できるFaust.jsというフレームワークが登場した
  • WPの管理画面上でプレビューできたりする便利機能やサイトの構築に役立つ便利なフックスを提供している
  • 使いこなすのに求められる技術レベルは割と高め
  • まだプレリリース版なので実戦投入は避けた方がいい(2021年9月現在)

基本的な部分しか触れていないので、あまり深いこと書けていないかもしれませんが、こんなことができるのかということをなんとなくでも感じ取っていただけたら嬉しいです。

冒頭でも触れたように、まだプレリリース版なので実戦投入は避けた方がいいと思いますが、正式版がリリースされたら、WPのヘッドレス化が今後進んでいきそうだなと感じました。

おまけ

久しぶりにいっぱい文章書いたのでちょっと疲れました。

余談も余談ですが、WordPressの静的化だと、Shifterがありますが、有料(無料版だと1週間しか使えない)だし、静的化によるパフォーマンスの最適化はできるけど、結局はサイトの構築自体はWPのみで使用可能な独自テーマ関数を使ってサイトの構築を行わないといけないので、個人の主観ですが正直微妙だと思っています。

→Shifter使っている人すいません!