電通総研 テックブログ

電通総研が運営する技術ブログ

Next.jsのgetServerSidePropsを使ってBlue/Greenデプロイでハマった件

こんにちは。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);
}

getServerSidePropsAPIからデータを受け取ってブラウザーに渡します。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のパスを変換する仕組みが必要ですが、なかなか難しいです。

解決方法

そもそもgetServerSidePropsAPI routesは両方ともサーバー側で実行されるため、getServerSidePropsAPI routesを呼びだすのは蛇足ですね。データの取得などのバックエンド処理は関数で作成し、getServerSidePropsで呼び出せばいいです。

修正後のコードはこちらです。API routesの作成をやめて、データベースの処理を関数にしました。

// logic/articles.ts

export async function fetchArticleNames(): Promise<string[]> {
  const articles: Article[] = await getArticlesFromDB();
  return articles.map((article) => article.name );
}

続いて、getServerSidePropsapi/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.taichiShodoで執筆されました