レンタルサーバーでもmicroCMS + Next.js でプレビュー機能を使いたい (SSR未使用)
ニッチすぎる記事です。
現在、自分のHPを以下の構成で運用しているんですが、
静的サイトジェネレーター : Next.js
コード管理 / ビルド : GitHub / GitHub Actions
Headless CMS : microCMS
ホスティング : ヘテムルレンタルサーバー
プレビュー機能を使うのって、
「Vercel」 などのホスティングサービス前提なんですね。。
まぁ、そういう商売だから当たり前か。
Node.jsが使える環境でSSRさせないといけないらしいので、
当然といえば当然なんですが、
色々なサービスを横断していて、さくっと実装するにはハードルが高いと思いました。
そこで試行錯誤して、なんとか普通のレンタルサーバーでも、プレビュー機能を使えるように
してみました。
実績紹介ページでpreviewを実装しよう
実績紹介の詳細ページを公開前に目視で確認したいのでプレビュー機能が欲しい。
かつ、microCMSにログインしていないユーザーにもプレしたい。
一覧
/works/
詳細
/works/osigoto1/
/works/osigoto2/
.
.
ファイル構成と実際のコード
今回は、works詳細ページのプレビュー機能を作るので
ページのURLは以下にしようと思います。(これは自由です)
/preview/works
ファイル構成(関係あるやつだけ)
src/
pages/
works/
[slug].tsx // works詳細ページ(参考)
preview/
works.tsx // ← 新しく作るworks詳細プレビューページ
プレビューページ全体のコード
import React, { useEffect, useState } from 'react'
import { createClient } from 'microcms-js-sdk'
// type
import { Post } from 'types/index'
// components
import Loader from 'components/loader' // ローディング
import WorksDetailBody from 'components/worksDetailBody' // 詳細ページのコンポーネント
type FetchData = {
post: Post | null // 現在の記事の情報
prev: { slug: string } | null // 1つ後の記事の情報 今回は関係無い
next: { slug: string } | null // 1つ前の記事の情報 今回は関係無い
}
// プレビューページのPage コンポーネント ////////////////////////////
export default function WorksPreview() {
// loadingを制御
const [isLoading, setIsLoading] = useState(true)
// fetchDataを入れる
const [postData, setPostData] = useState({} as FetchData)
useEffect(() => {
(async () => {
// APIから記事データを取得
const result = await previewWorksDetailProps()
if (result?.post) {
// プレビューのデータを取得出来たらデータをセット
setPostData(result)
setIsLoading(false)
return
}
// データが取得出来なかったら、works top へ リダイレクト
location.href = '/works/'
})()
}, [])
if (isLoading) return <Loader />
// ここは、「/works/[slug].tsx」 と同じものを表示するので、コンポーネント化しておきましょう
// プレビューページなので、isPreviewを渡す
return postData ? WorksDetailBody({ ...postData, isPreview: true }) : <></>
}
// microCMS のAPIから データを取得する関数 ////////////////////////////
const previewWorksDetailProps = async (): Promise<FetchData | null> => {
// urlからqueryパラメータを取得
const url = new URL(location.href)
const searchParams = url.searchParams
// プレビューに必要なパラメータのキー
const targetParams = ['contentId', 'draftKey', 'microcmsApiKey']
// queryパラメータを取得に3つの必要なパラメータがあるかチェック
const isExistParams = targetParams.every((p) => {
return searchParams.has(p)
})
// パラメータが足りなかったら処理を完了
// postデータがnullが返るとリダイレクトされる
if (!isExistParams) {
console.log('Invalid param')
return null
}
// microcms-js-sdkを使って、クライアントを作成
// パラメータから、それぞれキーの値を取得
const client = createClient({
serviceDomain: 'hogehoge123',
apiKey: searchParams.get('microcmsApiKey') ?? '',
})
const contentId = searchParams.get('contentId') ?? ''
const draftKey = searchParams.get('draftKey') ?? ''
// キーがあるので、下書き状態のデータを取得出来る
const post = await client
.get({
endpoint: 'works',
contentId, // これをセット
queries: {
draftKey, // これをセット
fields: 'title,slug,url,url2,date,body,production_period,credit,category,technology,slider',
},
})
.then((v) => {
return v
})
.catch((err) => {
return null
})
// postデータがnullが返ると処理を完了
// リダイレクトされる
if (post === null) {
console.log("Couldn't get the data")
return null
}
// ページャーの処理なので今回は関係無し start
const pager: any[] = await Promise.allSettled([
client
.get({
endpoint: 'works',
queries: {
draftKey,
limit: 1,
orders: '-date',
filters: `date[less_than]${post.date}`,
fields: 'slug',
},
})
.then((v) => {
return v?.contents?.length ? v.contents[0] : null
})
.catch((err) => {
return null
}),
client
.get({
endpoint: 'works',
queries: {
draftKey,
limit: 1,
orders: 'date',
filters: `date[greater_than]${post.date}`,
fields: 'slug',
},
})
.then((v) => {
return v?.contents?.length ? v.contents[0] : null
})
.catch((err) => {
return null
}),
]).then((results) =>
results.map((r) => {
if (r.status === 'fulfilled') {
return r.value
}
return null
})
)
// ページャーの処理なので今回は関係無し end
// 取得したpostデータを返却
return {
post,
prev: pager[0] ?? null,
next: pager[1] ?? null,
}
}
microcmsのプレビュー機能を使用
ここにプレビューページへのURLを貼り付けます。
microcmsApiKeyは、.envファイルに貼っているやつです。
APIからGETする時の認証で必要になります。これを設定
↓↓↓↓↓↓↓↓
https://hogehoge123.site/preview/works?contentId={CONTENT_ID}&draftKey={DRAFT_KEY}µcmsApiKey=●●●●●●●●●●●
設定画面
プレビュー画面
ボタンを押すと以下にリダイレクト
https://hogehoge123.site/preview/works?contentId=▲▲▲▲▲▲&draftKey=▼▼▼▼▼▼µcmsApiKey=●●●●●●●●●●●
プレビューページが表示される(パラメータが無い場合はリダイレクト)
パラメータから、認証キーを取得 → それを元にAPIを叩いて必要な情報をGET!
念の為プレビューページはクロールされないようにする
User-agent: *
Disallow: /preview/
// previewページはnoindexのメタを追加
{isPreview && <meta name="robots" content="noindex" />}
今回の実装の懸念点(1)
今回は、worksのプレビューを作ったが、
APIごとに、previewページを用意しないといけないのが面倒。
今回の実装の懸念点(2)
必要なパラメータは全てGETパラメータから取得する必要があり
プレビューURLをシェアする時に 「X-MICROCMS-API-KEY」 がURLに出る。
これはセキュリティ的に大丈夫なのか、的な不安がある。
今回は、自分の小規模サイトだし問題なさそう。
公式ブログによると
POSTなどには、別途 「X-WRITE-API-KEY」 が必要なんですね。
それぞれのキーで権限が別れていているのでその点は安心で、Goodですね!!
もし他に懸念点などあったら教えて頂けるとありがたいです。
Discussion