こんにちは。Xイノベーション本部ソフトウェアデザインセンターの陳です。
この記事ではNext.jsのgetServerSidePropsの利用でハマったことについて話します。
Server-side Rendering(SSR)とgetServerSideProps
Server-side Renderingはリクエストごとにサーバー側でレンダリングを行い、HTMLページを生成する機能です。
頻繁に変更されるデータを画面に表示させたい時はServer-side Renderingを利用します。Server-side Renderingを実現するには、サーバー側からデータを受け取る処理を行うgetServerSideProps関数を使います。
Next.jsの公式ドキュメントでは以下の例を掲載しています。
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Page
Blue/Greenデプロイでハマったこと
今回はNext.jsのAPI routesを使ってバックエンド処理のAPIを作成しました。
例えば、以下はデータベースからarticle名のリストを取得する処理を行うAPI routeです。
// pages/api/articles.ts
export default function handler(_req: NextApiRequest, res: NextApiResponse) {
const articles: Article[] = await getArticlesFromDB();
const articleNames = articles.map((article) => article.name );
res.status(200).json(articleNames);
}
getServerSidePropsでAPIからデータを受け取ってブラウザーに渡します。AxiosのbaseURLを環境変数で指定していました。
// pages/index.tsx
function Page({ articleNames }) {
// Render articles data...
}
export async function getServerSideProps() {
const baseURL = process.env.SERVER ? process.env.SERVER : "http://localhost:3000";
const res = await Axios.get<string[]>(`{baseURL}/api/articles`)
return { props: { articleNames: res.data } }
}
export default Page
このコードは一見問題なさそうですが、Blue/GreenデプロイでAxiosのbaseURLを正しく指定しないと問題が起きます(Fetch APIを利用する場合も同様です)。
Blue/Greenデプロイは、本番環境と検証環境を交互に入れ替えることにより、ダウンタイムを最小にするデプロイ手法です。
本来であれば、APIのパスはウェブサーバーと同一環境のものにならないといけません。例えば、本番環境のパスはhttps://example.comであれば、APIのパスはhttps://example.com/api/articlesになります。

今回はAxiosのbaseURLを本番環境のパスに指定しましたため、検証環境ウェブサーバーにアクセスしても、叩いたAPIは本番環境のものでした。

Blue/Greenデプロイはルーティング制御により、本番環境と検証環境の入れ替えが行われるため、APIのホスト名も環境と共に切り替える必要があります。

アクセスした環境は本番環境か検証環境かを識別し、APIのパスを変換する仕組みが必要ですが、なかなか難しいです。
解決方法
そもそもgetServerSidePropsとAPI routesは両方ともサーバー側で実行されるため、getServerSidePropsでAPI routesを呼びだすのは蛇足ですね。データの取得などのバックエンド処理は関数で作成し、getServerSidePropsで呼び出せばいいです。
修正後のコードはこちらです。API routesの作成をやめて、データベースの処理を関数にしました。
// logic/articles.ts
export async function fetchArticleNames(): Promise<string[]> {
const articles: Article[] = await getArticlesFromDB();
return articles.map((article) => article.name );
}
続いて、getServerSidePropsでapi/articlesを介さずに、fetchArticleNames()関数でarticle名のリストを取得するように修正しました。
// pages/index.tsx
function Page({ articleNames }) {
// Render article names...
}
export async function getServerSideProps() {
const articleNames = await fetchArticleNames();
return { props: { articleNames } }
}
export default Page
これで、Blue/GreenデプロイのAPIパス問題を回避し、コードもすっきりしました。
まとめ
この記事では、Next.jsのgetServerSidePropsを使ってBlue/Greenデプロイでハマったことについてまとめました。
getServerSidePropsはサーバー側で実行されますので、データの取得処理はAPI routesを介さずに関数で呼び出しましょう。
私たちは同じチームで働いてくれる仲間を探しています。今回のエントリで紹介したような仕事に興味のある方、ご応募お待ちしています。 - ソリューションアーキテクト
執筆:@chen.xinying、レビュー:@sato.taichi (Shodoで執筆されました)



