microCMS

ISR対応のAstroサイトにVercelのDraft Modeで画面プレビューを実装する

中野紘子

Astro + microCMSで画面プレビューを実装する方法は、これまでも以下の記事で解説してきましたが、

本記事では、VercelのDraft Modeを活用して、下書きコンテンツの画面プレビュー機能を実装する方法をご紹介します。

はじめに

ISRにおける画面プレビュー

Astroには、公式で提供されているVercelアダプター(@astrojs/vercel)があります。このアダプターを使うことで、Vercelにデプロイする際にISR(Incremental Static Regeneration)を簡単に設定することができます。

ISRとは、静的生成(SSG)と動的生成(SSR)のいいとこ取りのような仕組みで、一度生成されたページはCDNにキャッシュされつつ、一定時間後に自動で再生成されるという仕組みです。ヘッドレスCMSとの相性も良く、「一度表示したページを即座に返しつつ、最新データにも自動で追従できる」便利な手法です。

しかし、VercelにホスティングされたISRサイトは、公開されたコンテンツがCDNにキャッシュされるため、未公開の下書きコンテンツをプレビューすることはできません

VercelのDraft Modeで解決

この問題を解決するために用意されているのが、VercelのDraft Modeです。
VercelのDraft Modeは、ヘッドレスCMSなどと連携してプレビュー機能を実装するための仕組みです。

ブラウザに特定のCookie(__prerender_bypass)を付与することで、CDNキャッシュをバイパスし、最新の下書きコンテンツを取得・表示することができます。これにより、microCMSなどのヘッドレスCMSと組み合わせることで、下書き状態の記事を画面プレビューすることができます。

準備

利用サービス

1. microCMS
APIベースの日本製ヘッドレスCMSです。ブログのコンテンツ管理を担います。
2. Vercel
フロントエンド開発向けのプラットフォームで、サイト公開におけるインフラやCI/CDを担います。

ソフトウェアバージョン

下記のバージョンで開発を行っています。バージョンの差異によって若干機能が異なる可能性があります。

  • Astro 5.5.4
  • @astrojs/vercel 8.1.3
  • microcms-js-sdk 3.2.0

手順

1. microCMSのセットアップ

APIの作成

API名「お知らせ」、エンドポイント「news」というAPIを作成します。APIの型はリスト形式を選択してください。

APIスキーマを定義

タイトルと本文用のフィールドを用意します。

コンテンツを登録

APIが用意できたら、後ほど動作確認をするために「公開中の記事」と「下書き中の記事」を作成しておきましょう。

画面プレビュー設定

API設定より、画面プレビューの遷移先URLを以下に設定します。
https://yourdomain.com/draft?slug={CONTENT_ID}&dk={DRAFT_KEY}
yourdomain.comは環境に合わせて適宜変更してください

2. Astroプロジェクトのセットアップ

プロジェクトの作成

Astroのプロジェクトを作成します。

npm create astro@latest

以下ではastro-vercel-draftmodeというプロジェクト名で作成しておりますが、プロジェクト名や各種設定については任意の名前および設定で問題ありません。

作成したプロジェクトに移動し、microcms-js-sdk@astrojs/vercelをインストールします。

npm install microcms-js-sdk@latest @astrojs/vercel@latest

envファイルの作成

ルート直下に.envファイルを作成し、下記の情報を入力します。

MICROCMS_API_KEY=xxxxxxxxxx
MICROCMS_SERVICE_DOMAIN=xxxxxxxxxx

MICROCMS_API_KEY

microCMS 管理画面の「サービス設定 > API キー」から確認することができます。

MICROCMS_SERVICE_DOMAIN

microCMS 管理画面の URL(https://xxxxxxxx.microcms.io)の xxxxxxxx の部分です。

3. 実装

astro.config.mjsの設定

ルートにあるastro.config.mjsを次のように編集します。

// @ts-check
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';

// https://astro.build/config
export default defineConfig({
  output: 'server',
  adapter: vercel({
    isr: {
      expiration: 60,
      bypassToken: 'f3a13dad-b3d3-635a-94b7-eed969401a99',
      exclude: ['/draft'],
    },
  }),
});

・最大60秒間キャッシュされ、それ以降のアクセス時に再生成が行われます
bypassToken の値は任意の文字列でOKですが、32文字以上である必要があります
/draftでキャッシュが返らないように除外しています(ISRの対象外となります

SDK利用準備

src/libs/microcms.tsを作成し、SDKの利用準備をしておきます。

// SDK利用準備
import { createClient } from 'microcms-js-sdk';

export const client = createClient({
  serviceDomain: import.meta.env.MICROCMS_SERVICE_DOMAIN,
  apiKey: import.meta.env.MICROCMS_API_KEY,
});

一覧ページの作成

src/pages/index.astroを以下のように編集します。エンドポイントnewsから全ての記事データを取得しています。

---
import Layout from "../layouts/Layout.astro";
import { client } from "../libs/microcms";

// 型定義
type News = {
  title: string;
  body: string;
};

//エンドポイント "news" から全ての記事データを取得
const news = await client.getAllContents<News>({
  endpoint: "news",
});
---

<Layout>
  <main class="main">
    <h1>お知らせ一覧</h1>
    <ul>
      {
        news.map((content) => (
          <li>
            <a href={content.id}>{content.title}</a>
          </li>
        ))
      }
    </ul>
  </main>
</Layout>

<style>
  .main {
    margin: 0 auto;
    max-width: 960px;
  }
</style>

トップページにアクセスすると、公開済みの記事が一覧で表示されるようになっています。

詳細ページの作成

次に詳細ページを表示してみましょう。src/pages/[newsId].astroを作成します。

---
import type { MicroCMSQueries } from 'microcms-js-sdk';
import Layout from "../layouts/Layout.astro";
import { client } from "../libs/microcms";

// 型定義
type News = {
  title: string;
  body: string;
};

// 指定されたcontentIdのデータを取得する関数
const getNewsDetail = async (
  contentId: string,
  queries?: MicroCMSQueries
) => {
  return await client.getListDetail<News>({
    endpoint: 'news',
    contentId,
    queries,
  });
};

// パラメータからnewsIdを取得
const { newsId } = Astro.params;
// 該当ニュースのデータを取得
const news = await getNewsDetail(newsId as string);
---

<Layout>
  <main class="main">
    <h1>{news.title}</h1>
    <div set:html={news.body}></div>
  </main>
</Layout>

<style>
  .main {
    margin: 0 auto;
    max-width: 960px;
  }
</style>

詳細ページにアクセスすると、作成したコンテンツのタイトルと本文が表示されます。

画面プレビューの実装

それではいよいよ画面プレビューの実装をしていきましょう。src/pages/draft.astroを作成します。

---
// パラメータから "dk"(draftKey)と "slug"(コンテンツID)を取得
const draftKey = Astro.url.searchParams.get("dk") ?? undefined;
const slug = Astro.url.searchParams.get("slug") ?? undefined;

// draftKeyとslugの両方が存在する場合にDraft Modeを有効化
if (draftKey && slug) {
  Astro.cookies.set('__prerender_bypass', 'f3a13dad-b3d3-635a-94b7-eed969401a99');
  Astro.cookies.set('__preview_slug', slug);
  Astro.cookies.set('__preview_dk', draftKey);
}

// slugが存在する場合は該当記事ページへリダイレクト
return Astro.redirect(`/${slug}`);
---

__prerender_bypassはDraft Modeを有効化するためのCookieです。astro.config.mjsにて設定した値を指定してください
・リダイレクト後に下書きコンテンツ取得のためのdraftKeyが必要なので、__previewdkというCookieにセットしています
・該当記事のDraft Modeだということを後に検証するために、コンテンツIDも__preview_slugというCookieにセットしています

詳細ページを変更

src/pages/[newsId].astroを次のように変更します。

---
import type { MicroCMSQueries } from 'microcms-js-sdk';
import Layout from "../layouts/Layout.astro";
import { client } from "../libs/microcms";

type News = {
  title: string;
  body: string;
};

const getNewsDetail = async (
  contentId: string,
  queries?: MicroCMSQueries
) => {
  return await client.getListDetail<News>({
    endpoint: 'news',
    contentId,
    queries,
  });
};

const { newsId } = Astro.params;

// Cookieから、プレビュー用のslugとdraftKeyを取得
const slug = Astro.cookies.get('__preview_slug');
const draftKey = Astro.cookies.get('__preview_dk');

// Cookieに保存されたslugとパラメータのnewsIdが一致しない場合、プレビュー終了(Cookie削除)
if (slug?.value !== newsId) {
  Astro.cookies.delete('__prerender_bypass');
  Astro.cookies.delete('__preview_slug');
  Astro.cookies.delete('__preview_dk');
}

// 該当ニュースのデータを取得(draftKeyがあれば下書きを取得)
const news = await getNewsDetail(newsId as string, { draftKey: draftKey?.value });
---

// 略

・Cookieにあるslugの値とnewsIdの値が異なる場合は想定しているプレビュー画面から移動していると見て、関連Cookieをすべて削除し、Draft Modeを終了させています。
getNewsDetailの第二引数はクエリパラメータを受けられるようになっているので、そこにdraftKeyを渡しています。

4. Vercelにデプロイする

Vercelにデプロイして、ページにアクセスしてみましょう。
Vercelへのデプロイ方法方法は以下の記事を参考にしてください。
microCMS + Next.jsでJamstackブログを作ってみよう(Next.js 15 ver.)

デプロイ後、microCMSの「画面プレビュー」をクリックし、下書きコンテンツが無事表示されれば成功となります。

下書き中のページが表示され、X-Vercel-CacheBYPASSとなっていることが確認できました。

💡注意

下書きモードが継続される

一度プレビュー用URLにアクセスしてCookieが付与されると、以降も同じブラウザでは常に下書き状態のページが表示され続けます
そのため、必要に応じてプレビューモードを終了するためのCookie削除ボタンなどを用意しておくと安心です。

プレビューURLの共有

プレビュー用のURLを共有する場合は、必ず以下のような元のURL(リダイレクト前のURL)を共有してください
https://yourdomain.com/draft?slug={CONTENT_ID}&dk={DRAFT_KEY}
リダイレクト後の表示URL(例:https://yourdomain.com/{CONTENT_ID})を共有しても、閲覧者のブラウザには必要なCookieが設定されていないため、下書きコンテンツは表示されません

おわりに

今回は、VercelのDraft Modeを活用して、下書きコンテンツの画面プレビュー機能を実装する方法をご紹介しました。ISRサイトのホスティングにVercelを使用する場合は、ぜひ参考にしてみてください。

なお、この記事ではslugdraftKeyをCookieで直接渡す方法をご紹介しましたが、より良い実装方法があれば、ぜひ教えていただけると嬉しいです!

まずは、無料で試してみましょう。

APIベースの日本製ヘッドレスCMS「microCMS」を使えば、 ものの数分でAPIの作成ができます。

microCMSを無料で始める

microCMSについてお問い合わせ

初期費用無料・14日間の無料トライアル付き。ご不明な点はお気軽にお問い合わせください。

お問い合わせ

microCMS公式アカウント

microCMSは各公式アカウントで最新情報をお届けしています。
フォローよろしくお願いします。

  • X
  • Discord
  • github

ABOUT ME

中野紘子
制作会社にてフロントエンドエンジニアの経験を経て、現在はmicroCMSでマーケターをしています。趣味はゲームと散歩です。