はじめに
Next.jsのDraft Modeと呼ばれる手法を使えば、ヘッドレスCMSの下書きコンテンツを公開されているアプリケーションで確認することができます。
この記事ではmicroCMSを使って、Draft Modeを使った機能を実装します。
コードの全貌はGitHubで確認できます。
microCMSの使用はこれが初めてなので、一部不要な作業を行なっている恐れがあります
Next.jsの環境作成
今回利用するNext.jsの環境についてこだわる箇所がないので、自動インストールで環境を準備します。
npx create-next-app@latest
その後、favicon.ico
の削除などNext.jsの要素を削除します。
@tailwind base;
@tailwind components;
@tailwind utilities;
import type { Metadata } from "next";
import { Noto_Sans_JP } from "next/font/google";
import "./globals.css";
const notoSansJP = Noto_Sans_JP({
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Draft Mode Example",
description: "Example of using draft mode in Next.js",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ja">
<body className={notoSansJP.className}>
{children}
</body>
</html>
);
}
export default function Home() {
return (
<main className="flex flex-col items-center justify-center w-screen h-screen">
<section className="rounded-lg shadow-lg p-4 flex flex-col gap-4 bg-slate-300">
<h1 className="text-lg font-bold">お知らせ</h1>
<ul>
<li>
<div className="flex justify-between gap-2 items-end">
<p>ファンタスティックな機能を追加しました</p>
<p className="text-sm text-gray-800">2021-01-03</p>
</div>
</li>
<li>
<div className="flex justify-between gap-2 items-end">
<p>お知らせのページを追加しました</p>
<p className="text-sm text-gray-800">2021-01-02</p>
</div>
</li>
<li>
<div className="flex justify-between gap-2 items-end">
<p>サイトを公開しました</p>
<p className="text-sm text-gray-800">2021-01-01</p>
</div>
</li>
</ul>
</section>
</main>
);
}
app/page.tsx
はmicroCMSから取得したお知らせを表示したいので、簡単に見た目を作っています。
microCMSの環境作成
自動でお知らせが一つ作成されているはずです。それをNext.jsで確認するために、次はお知らせの一覧を取得する処理を記述します。
今回は簡単にデータを表示させるだけなのでfetch
を用いてデータを取得します。fetch
の他にもsdkからデータを取得することも可能なようです。
.env
等にMICROCMS_API_KEY
を登録すると以下のgetData
でお知らせの一覧を取得できます。
type News = {
contents: {
id: string;
title: string;
publishedAt?: string;
}[];
totalCount: number;
offset: number;
limit: number;
}
async function getData(): Promise<News> {
const url = 'https://draft-mode.microcms.io/api/v1/news';
const res = await fetch(url, {
headers: {
'X-MICROCMS-API-KEY': process.env.MICROCMS_API_KEY ?? '',
},
});
return res.json();
}
const dtf = new Intl.DateTimeFormat('ja-JP', {
year: 'numeric',
month: 'numeric',
day: 'numeric',
});
export default async function Home() {
const { contents } = await getData();
return (
<main className="flex flex-col items-center justify-center w-screen h-screen">
<section className="rounded-lg shadow-lg p-4 flex flex-col gap-4 bg-slate-300">
<h1 className="text-lg font-bold">お知らせ</h1>
<ul>
{contents.map((content) => (
<li key={content.id}>
<div className="flex justify-between gap-2 items-end">
<p>{content.title}</p>
<p className="text-sm text-gray-800">
{content.publishedAt
? dtf.format(new Date(content.publishedAt))
: 'draft'}
</p>
</div>
</li>
))}
</ul>
</section>
</main>
);
}
次に下書き状態のお知らせを作ります。
下書きのお知らせはそれぞれがdraftKey
を持っており、それをfetch
のリクエスト時にパラメータとして含めると下書き状態のお知らせも併せて取得できるようです。draftKey
はお知らせの詳細ページの左上に書かれています。
async function getData(): Promise<News> {
const url = 'https://draft-mode.microcms.io/api/v1/news';
const res = await fetch(`${url}?draftKey=D9No73sxg5`, {
headers: {
'X-MICROCMS-API-KEY': process.env.MICROCMS_API_KEY ?? '',
},
});
return res.json();
}
先ほどのgetData
をこれに変更すると、下書き状態のお知らせも一覧に出てきたはずです。
Draft Mode
準備が完了しました。Draft Modeを利用して、特定のリクエストを送った時だけ指定した下書きのお知らせを含んだUIを表示させます。
まずは、Router Handlerで特定のリクエストを行う宛先を作成します。
import { draftMode } from 'next/headers'
export async function GET() {
const draft = await draftMode()
draft.enable()
return new Response('Draft mode is enabled')
}
Draft Modeを有効にしてテキストレスポンスを返すだけのAPIです。
このAPIにアクセスしてみると、レスポンスヘッダーのSet-Cookie
に__prerender_bypass
が含まれています。
これがあることで後続の処理でNext.jsはDraft Modeが有効となっていることを判別します。
通常のレスポンスには含まれないことはdraft.enbale()
を消すとわかります。
次に、このAPIにdraftKey
とsecret
を受け取って、正常であれば/
にリダイレクトするような処理を追加します。
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const secret = searchParams.get('secret')
const draftKey = searchParams.get('draftKey')
if (secret !== process.env.MICROCMS_API_KEY || !draftKey) {
return new Response('Invalid token', { status: 401 })
}
const draft = await draftMode()
draft.enable()
redirect('/?draftKey=' + draftKey)
}
secret
を受け取って検証するようにしたのは、draftKey
を適当に入力して下書き状態のお知らせを閲覧されることを防ぐためです。
/
にdraftKey
が渡ってくるようになりました。Draft Modeが有効なときはdraftKey
の下書きも表示するようにしましょう。
import { draftMode } from "next/headers";
type News = {
contents: {
id: string;
title: string;
publishedAt: string;
}[];
totalCount: number;
offset: number;
limit: number;
}
async function getData({ draftKey }: { draftKey?: string }): Promise<News> {
const { isEnabled } = await draftMode()
const url = isEnabled
?'https://draft-mode.microcms.io/api/v1/news?draftKey=' + draftKey
: 'https://draft-mode.microcms.io/api/v1/news';
const res = await fetch(url, {
headers: {
'X-MICROCMS-API-KEY': process.env.MICROCMS_API_KEY ?? '',
},
});
return res.json();
}
const dtf = new Intl.DateTimeFormat('ja-JP', {
year: 'numeric',
month: 'numeric',
day: 'numeric',
});
export default async function Home({
searchParams,
}: {
searchParams: Promise<{
draftKey: string | undefined;
}>;
}) {
const draftKey = (await searchParams).draftKey;
const { contents } = await getData({ draftKey });
return (
<main className="flex flex-col items-center justify-center w-screen h-screen">
<section className="rounded-lg shadow-lg p-4 flex flex-col gap-4 bg-slate-300">
<h1 className="text-lg font-bold">お知らせ</h1>
<ul>
{contents.map((content) => (
<li key={content.id}>
<div className="flex justify-between gap-2 items-end">
<p>{content.title}</p>
<p className="text-sm text-gray-800">
{content.publishedAt
? dtf.format(new Date(content.publishedAt))
: 'draft'}
</p>
</div>
</li>
))}
</ul>
</section>
</main>
);
}
Draft Modeが有効であることはisEnabled
で判断します。isEnabled
がtrue
な時にだけ受け取ったdraftKey
を含めたリクエストをmicroCMSに送信します。
app/api/draft-news/route.ts
でdraft.enable()
を削除したときは動作しないことを安全のため確認してください。
これによって通常は投稿済みのお知らせしか表示されないが、お知らせを管理する人が限定的にmicroCMSのAPIキーとdraftKey
を用いて下書きのお知らせを含めた表示を確認する仕組みができました。
おわりに
私はCMSを使った経験がなかったので、ドキュメントを読むだけではイマイチ理解を進められませんでした。
実際に動かしてみることで、Draft Modeがどのようなものか、どのようなところが嬉しいのかが理解できてよかったです。
開発者とCMSの管理者が別である場合、お知らせを投稿する前の見た目の調整は大変なはずなので、この機能があるとかなり業務の効率化が図られるのではないでしょうか。