microCMS

Next.jsのPreview Mode+Vercelでプレビュー機能を実現する

エンジニアリング
2020/05/04 柴田 和祈

こんにちは、柴田です。
Next.js 9.3からPreview Modeがサポートされました。
Preview Modeは完全にヘッドレスCMS向けに作られた機能です。

静的生成によるJamstack構成は高パフォーマンス、高セキュリティを実現しますが、プレビューの実現には工夫する必要がありました。
下書き状態の記事を確認する際に、その都度ビルドを走らせてページを生成するのは時間がかかりすぎてしまうためです。

そこで今回のNext.jsによるPreview Modeの登場です。
Preview Modeを利用すると静的生成ロジックをうまく利用してサーバーサイドレンダリング(SSR)を行い、プレビューを実現することができます。

API Routes

まず前提として、Next.jsにはサーバーレス関数を起動できるAPI Routes機能があります。
`/pages/api/`以下のファイルはサーバーレス関数として動作します。
Preview Modeではこの機能を利用します。

プレビュー用関数の作成

次のような関数(/pages/api/preview.js)を用意します。

import fetch from 'node-fetch';

export default async (req, res) => {
  if (!req.query.slug) {
    return res.status(404).end();
  }
  const content = await fetch(
    `https://xxxxxx.microcms.io/api/v1/blog/${req.query.slug}?fields=id`,
    { headers: { 'X-API-KEY': process.env.apiKey || '' } }
  )
  .then(res => res.json()).catch(error => null);

  if (!content) {
    return res.status(401).json({ message: 'Invalid slug' });
  }

  res.setPreviewData({
    slug: content.id,
    draftKey: req.query.draftKey,
  });
  res.writeHead(307, { Location: `/${content.id}` });
  res.end('Preview mode enabled');
};


ブラウザから `/api/preview` にアクセスするとこの関数が起動します。
クエリとして渡ってくる slug は記事のID、draftKey は下書き用の draftKey に当たります。
まず、slugが存在しない場合は404を返します。
公式のチュートリアルでは、CMSとNext.js間で共通鍵による認証もしています。
よりセキュアにしたい場合はその処理を入れてください。

次に、slugが正しいものであるか実際にmicroCMSのAPIを呼び出してチェックします。
その後、`res.setPreviewData()` に渡した引数が `getStaticProps` の引数である `context` から受け取ることができます。
最後に、本来の記事のパスにリダイレクトさせます。
この際に、オープンリダイレクトの脆弱性回避のために、`req.query.slug` ではなく `content.id` を使うようにします。

リダイレクト後はプレビュー用に `__prerender_bypass``__next_preview_data` というcookieが付与されます。

ページ側での処理

リダイレクトして記事ページに辿り着いた後は、`getStaticPath` は通らずに `getStaticProps` が動作します。
先ほどのcookie付きでこのページに辿り着いた場合、`getStaticProps` の引数である `context` に対し、

  • `context.preview` はtrue
  • `content.previewData` には `setPreviewData` に与えた引数

が格納されています。

これにより `slug``draftKey` を受け取ることができるので、microCMSのGET APIを叩く際にクエリとして指定し、下書き状態のコンテンツを取得することができます。

export const getStaticProps = async (context) => {
 const slug = context.params?.slug;
 const draftKey = context.previewData?.draftKey;
 const content = await fetch(
  `https://xxxxxx.microcms.io/api/v1/blog/${slug}${
   draftKey !== undefined ? `?draftKey=${draftKey}` : ''
  }`,
  { headers: { 'X-API-KEY': process.env.apiKey || '' } }
 )
  .then((res) => res.json());
  return {
    props: {
      content
    }
  };
};


実際の動きは以下のようになります。

  1. `/api/preview?slug={slug}&draftKey={draftKey}` にアクセス
  2. プレビュー用のcookieが発行され、`/{slug}` にリダイレクト
  3. `getStaticProps``slug``draftKey` を受け取ることができるので、下書き情報を取得
  4. 画面に表示


また、プレビューかどうかのフラグがあるので、画面側で「現在プレビュー中です」などの表示も可能です。
Cookieを無効にするためにはCookieクリア用のAPI routeを別で用意し、画面から呼び出すのが良いでしょう。

export default (req, res) => {
  res.clearPreviewData();
}


CMS側の設定

次にmicroCMS側からプレビュー画面へのつなぎ込み設定を行います。
API設定 > 画面プレビューから設定が可能です。



ここでは、コンテンツ編集画面の画面プレビューリンクをクリックした際の遷移先を指定できます。
先ほどのプレビュー機能を用いるために、以下のように入力します。

https://some-blog-domain.com/api/preview?slug={CONTENT_ID}&draftKey={DRAFT_KEY}


以上でつなぎ込みは完了です。
下書きの状態でコンテンツ編集画面の画面プレビューボタンをクリックすることでプレビュー画面を閲覧することができます。


デプロイ

API Routes機能はサーバーレス関数を起動するため、デプロイ先のサーバーはホスティングだけでなく、サーバーロジックの処理を行う必要があります。
Next.jsのPreview Modeでは内部の処理でcookieの付与等も行うため、この機能を完全に活かすためにはVercel (Now)にデプロイするのが最適でしょう。

Vercelとは

VercelはNext.jsを開発しているVercel社(旧ZEIT)のサービスで、Webサイトのホスティング機能やサーバーレス関数をデフォルトで備えている特徴があります。
SSR、SPA、SSGのすべてに対応している点が強いですね。
また、個人開発は基本的に全て無料で使うことができます。(GitHubのOrganization連携は無料プランではできませんでした)

方法

Gitの連携か、nowコマンドによるcliデプロイのどちらかになります。
Git連携はVercelのダッシュボードにしたがって進めればOKです。
コマンドでやる場合は以下の3コマンドでいけます。

$ npm i -g now

// メールアドレス入力、確認
$ now login

// カレントディレクトリをデプロイ
$ now


Vercel側の設定画面で、ビルドコマンド等を指定する箇所がありますが、ビルドコマンドを特に変更していない場合はそのままでOKです。
`npm run build && npm run export` と入力してしまうと、API Routesが動かなくなるのでお気をつけください。

おわりに

Next.jsのPreview Modeを使って、microCMSのプレビュー機能を実装してみました。
今まではプレビュー用にルーティングをし、クライアントサイドから下書き用のAPIを呼び出してプレビュー画面を用意していましたが、その必要がなくなりました。
プレビュー画面へのアクセスに対してはサーバーサイドで判定を行うことができるため、よりセキュアになります。
本番環境をそのまま下書きプレビュー環境にできるので素晴らしいですね!
ぜひ試してみてください。

-----

microCMSは日々改善を進めています。
ご意見・ご要望は管理画面右下のチャット、公式Twitterメールからお気軽にご連絡ください!
引き続きmicroCMSをよろしくお願いいたします!

ABOUT ME

柴田 和祈
microCMSのデザイン、フロントエンド担当 / ex Yahoo / 2児の父 / 著書「React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで 」 / Jamstack