はじめに
microCMSのプレビュー機能について、意外と参考記事がなかったのと、忘れてしまいがちな機能だと思ったので記事を書くことにしました。
よく考えたらヘッドレスCMSはバックエンドとフロントエンドが分離しているので、管理画面からプレビューができないのは当たり前ですが、WordPressに慣れていると忘れやすいと思います。
筆者もほぼ実装が終わった時に、テストを兼ねてなんとなく触っていたら、「これ記事投稿する時、コンテンツのプレビューとかなくて困るくね、、、?」となってそこで初めてプレビュー機能の存在を知りました。
実装は公式の実装例をベースにしています。
https://blog.microcms.io/nuxt-jamstack-preview/
この記事を読むとわかること
- Nuxt3 (SSG) でmicroCMSの下書き記事をプレビューする方法
- 1つのプレビューページで複数のコンテンツタイプを出し分ける実装
- フロントエンドにAPIキーを公開する際のセキュリティに関する考え方
実装の全体像
今回は、以下のような流れでプレビュー機能を実現します。
-
microCMS側の設定:
プレビューボタンを押した際の遷移先URLを設定します。このとき、どのコンテンツか識別するための情報をクエリパラメータで渡すのがポイントです。 -
Nuxt3側の実装:
プレビュー専用のページ (/pages/preview.vue
) を作成します。 -
データの取得と表示:
プレビューページでクエリパラメータを受け取り、クライアントサイドからmicroCMS APIを直接叩いて下書きデータを取得します。受け取ったコンテンツの種類に応じて、表示するコンポーネントを動的に切り替えます。(microCMSにコンテンツを数種類用意している前提です。)
microCMS側の設定
まずは、microCMSの管理画面でプレビュー用のURLを設定します。
対象APIの「API設定」>「プレビュー」画面に移動し、以下のようにURLを設定します。
https://your-domain/preview/?id={CONTENT_ID}&draftKey={DRAFT_KEY}&contentType=〇〇
重要なポイント ✨
-
{CONTENT_ID}
と{DRAFT_KEY}
は、microCMSが自動でコンテンツIDと下書きキーに置換してくれます。 -
&contentType=〇〇
の部分ですが、ここにAPIのendpoint名(例:news
,blog
など)をハードコードしておくことで、Nuxt側でどのコンテンツのプレビューなのかを判別できるようになります。
例えば、「お知らせ」API(endpoint: news
)なら、以下のように設定します。
https://your-domain/preview/?id={CONTENT_ID}&draftKey={DRAFT_KEY}&contentType=news
この設定をプレビューしたい全てのAPIで行います。
Nuxt3側の実装 (preview.vue
)
次に、Nuxt3プロジェクトの pages
ディレクトリに preview.vue
というファイルを作成し、以下のコードを記述します。
<template>
<div>
<Header />
<main>
<NewsDetail
v-if="contentType === 'news'"
:news-item="newsData"
:pending="pending"
:error="error"
/>
<BlogDetail
v-if="contentType === 'blog'"
:blog-item="blogData"
:pending="pending"
:error="error"
/>
</main>
<Footer />
</div>
</template>
<script setup lang="ts">
import type { News } from '~/types/news';
import type { Blog } from '~/types/blog';
// 1. クエリパラメータを取得
const route = useRoute();
const { contentType, id, draftKey } = route.query as { contentType: string, id: string, draftKey: string };
// 2. 環境変数を取得
const config = useRuntimeConfig();
const apiKey = config.public.microcmsApiKey;
const serviceDomain = config.public.microcmsServiceDomain;
// 3. microCMSから下書きデータを取得
const { data, error, pending } = useFetch<News | Blog>(
// contentTypeを使ってAPIのパスを動的に生成
`/api/v1/${contentType}/${id}`,
{
baseURL: `https://${serviceDomain}.microcms.io`,
headers: {
'X-MICROCMS-API-KEY': apiKey,
},
query: {
// draftKeyを付与して下書きデータをリクエスト
draftKey: draftKey,
}
}
);
// 4. contentTypeに応じてデータを型付けしてコンポーネントに渡す
const newsData = computed(() => contentType === 'news' ? data.value as News : null);
const blogData = computed(() => contentType === 'blog' ? data.value as Blog : null);
</script>
コード解説 👨💻
<script setup lang="ts">
1. クエリパラメータの取得
useRoute()
を使って、microCMSから渡されたURLのクエリパラメータ (id
, draftKey
, contentType
) を取得します。
2. 環境変数の取得
useRuntimeConfig()
を使って、.env
ファイルなどに設定したmicroCMSのサービスドメインとAPIキーを安全に読み込みます。(public
に設定する必要がある点に注意してください)
3. microCMSからのデータ取得
useFetch
を使って、クライアントサイドからmicroCMS APIにリクエストを送信します。
-
APIのパス:
${contentType}
を使うことで、news
やblog
といったコンテンツの種類に応じて動的にパスを組み立てています。 -
headers:
X-MICROCMS-API-KEY
に取得したAPIキーを設定します。 -
query:
draftKey
をクエリパラメータとして渡すことで、公開前の下書きデータを取得できます。
4. データの型付けとコンポーネントへの受け渡し
useFetch
で取得した data
は、複数のコンテンツタイプのいずれかの型を持つ可能性があります。そこで computed
を使い、contentType
の値に応じて適切な型に変換し、各詳細表示コンポーネント (<NewsDetail>
など) に props として渡しています。
v-if
ディレクティブを使って、contentType
の値に応じて表示するコンポーネントを切り替えています。
これにより、preview.vue
というたった1つのファイルで、サイト内のすべてのコンテンツのプレビューに対応できます。管理がとても楽になりますね!
各コンテンツの詳細ページ全体をBlogDetailのようにコンポーネント化することで、プレビューと実際の表示で差分が出ないようにしています。
ちなみに、詳細ページは下記のようにコンポーネントを一つ呼び出すだけのシンプルな構成になっています。
<template>
<div>
<Header />
<main>
<BlogDetail
:blog-item="blogItem"
:pending="pending"
:error="error"
/>
</main>
<Footer />
</div>
</template>
<template>
セキュリティに関する考察:APIキーをフロントに公開しても大丈夫?
今回の実装では、プレビュー機能のためにクライアントサイド(ブラウザ)から直接microCMSのAPIを叩いています。そのため、Nuxtの環境変数 runtimeConfig.public
を経由して、ブラウザの開発者ツールなどでAPIキーが見える状態になります。
「APIキーを公開しちゃって大丈夫なの?」と不安に思うかもしれません。
筆者はまだエンジニア経験が2ヶ月の超新人なので、めちゃくちゃ不安になって、社内のエンジニア全員に意見を聞きに行きました。
結論から言うと、「microCMSの権限管理を正しく設定していれば、リスクは許容範囲内」と判断しました。
なぜ許容できるのか
-
microCMSの強力なAPIキー権限設定:
microCMSでは、APIキーごとに非常に細かく権限を設定できます。
プレビューに使うAPIキーに対して、以下のようにGETのみを与えるようにしましょう。
こう設定しておけば、このキーを使ってできることは記事の取得だけに限られます。取得できる記事は既に公開されているものなので、特に困りません。
ただ、想定されるケースとして、誰かが誤ってここの権限を操作してしまうみたいなケースはおおいにありうるので、対策は必須かと思います。
特にmicroCMSの管理画面はビジネスサイドの人をはじめとして複数人が触るものなので、権限管理など必要になりそうです。
この考え方は、microCMS公式のQ&Aページでも紹介されています。
参考
まとめ
今回は、Nuxt3 (SSG) とmicroCMSで、単一ファイルで複数のコンテンツに対応できるプレビュー機能を実装する方法をご紹介しました。
-
contentType
をクエリパラメータで渡すことで、プレビューページを共通化 -
useFetch
でクライアントサイドから下書きデータを取得 - フロントへのAPIキー公開は、microCMSの権限設定をしっかり行うことでリスクを管理
APIキーに関する考え方は企業によってさまざまだと思うので、情シスと相談してみるのがいいかと思います。
余談ですが、microCMSを知らない人が0から実装を初めて、「バックエンドとフロントエンドが分離しているってことは、記事を投稿してbuildしてデプロイするまでに画面のイメージを確認できないから、プレビュー機能とか必要じゃね?」って気づける人って、相当センスのいい人な気がします。
どう使われるかイメージしながら作るのって大事ですね。勉強になりました🔥
あと、microCMSの記事全然なくて困るので皆さん投稿してください〜😭