こんにちは。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で執筆されました)