Next.js でSSR,SSGのみプライベートネットワークの通信を使うには環境変数で host 指定がおすすめ

Next.js も API も同一のネットワーク上にサーバーがある場合、SSGやSSRをするときにAPIとの通信はプライベートネットワークを使いたくなることがあります。
Ajax や fetch 使用時の host を分ければいいのですが、Next.js が提供する機構で極力シンプルに要件を満たしてみようと思います。

結論

結論はこの記事のタイトルのとおりクライアントサイドとサーバーサイド向けの host 指定する環境変数を容易するのがアプリケーションコードへの影響が少なくおすすめです。
.env ファイルに下記のような環境変数を用意します(NEXT_PUBLIC_ 接頭辞の有無が重要です)

  • サーバーサイド用ホスト指定 API_SERVER_HOST
  • クライアントサイド用ホスト指定 NEXT_PUBLIC_API_SERVER_HOST

Axios での使用例

const instance = axios.create({
  baseURL: `https://${API_SERVER_HOST||NEXT_PUBLIC_API_SERVER_HOST}/api/v1`
});

これは NEXT_PUBLIC_ 接頭辞がついた環境変数のみビルド時にクライアントサイドのコードに環境変数が展開される Next.js の機構を利用しています。

それ以外のアプローチ

前述の結論で分かる方はこの後はとくに読む必要がないかもしれませんが解説していきます。
考えられるアプローチは下記になると思います。

  • サーバーで実行するAPIコールを特定して処理を変える
  • 実行環境に応じて使用する host を変更する

サーバーで実行するAPIコールを特定して処理を変える

Next.js の設計にのっている場合 SSR, SSG 時にサーバーサイドで API コールが実行されるのは下記に集約できるはずです。

  • getServerSideProps
  • getStaticProps
  • getStaticPath

なので、この内部で使われる Ajax や fetch では host がプライベートネットワークになるように処理を変えれば対応できます。
しかし、この方法だとAPIコールをする場合に常にサーバーサイドでの実行かクライアントサイドでの実行かを意識しなければならず、開発規模が大きくなってきたりチームメンバーが増えてくるとストレスになること間違いなしです。
(冷静に考えれば当たり前でも、ネットワーク要件でバタバタと改修することになると思いもよらないアプローチを取りそうになります。冷静さを欠いていて危なく採用しそうになりました。)

実行環境に応じて使用する host を変更する

Next.js 以前から SSR などで Isomorphic なコードを書いている方はこちらをすぐに思いつくでしょう。
window の有無などブラウザと node.js 環境の違いを起点に host 指定を変更すればOKです。

typeof window !== "undefined"
=> false // サーバーサイドの場合 window が存在しないので false が返る

Axios の場合、こんな感じで指定してあげることでアプリケーション内の全通信を一括でコントロールできます。

const instance = axios.create({
  baseURL: `https://${typeof window !== "undefined" ? "example.com" : "example.internal"}/api/v1` 
});

Next.js に限らず Node.js で SSR と CSR で通信を分けたいときはこうするのが意図もわかりやすく、設定一箇所で済むので見通しの良いコードになります。

環境変数の使用

実際のアプリケーション開発では本番環境やテスト環境、ローカル環境で API サーバーは別に用意することがほとんどです。
そうなると host はソースコード内に記述するわけにはいけません。そのため .env ファイルなどで下記のように指定して環境変数から host の情報を渡す用にするのが一般的ではないでしょうか。

.env ファイル

API_SERVER_HOST=example.com
INTERNAL_API_SERVER_HOST=example.internal

Axios の例

const { API_SERVER_HOST, INTERNAL_API_SERVER_HOST } = process.env

const instance = axios.create({
  baseURL: `https://${typeof window !== "undefined" ? API_SERVER_HOST : INTERNAL_API_SERVER_HOST}/api/v1` 
});

NEXT_PUBLIC_ 変数の利用

前述のとおり Next.js には NEXT_PUBLIC_ 接頭辞をつけたものだけブラウザサイドの js に渡されるという機構があります。

これを利用することで window の有無を確認せずとも環境変数の有無でサーバーサイドとクライアントサイドで利用する host の切り替えが可能になります。

API_SERVER_HOST=example.com
NEXT_PUBLIC_API_SERVER_HOST=example.internal

Axios の例

const API_SERVER_HOST = process.env.API_SERVER_HOST
const NEXT_PUBLIC_API_SERVER_HOST = process.env.NEXT_PUBLIC_API_SERVER_HOST

const instance = axios.create({
  baseURL: `https://${NEXT_PUBLIC_API_SERVER_HOST || API_SERVER_HOST}/api/v1` 
});

まとめ

今回の手法は普段お仕事を手伝ってもらっている MATSUKAZE. 及川さんに助言いただきました。及川さんありがとうございます!

NEXT_PUBLIC_ の挙動を知っていてもその使いみちやパターンを理解できていないと無駄なコードを書いてしまうなーと実感したので記事にまとめてみました。
今回は粒度の小さな Tips でしたが、これからもフレームワークやライブラリの便利な機能とそれを適切に使う方法を学んでいき共有していきます。

開発のお悩み、フロントエンドから解決しませんか?

あなたのチームのお悩みはなんですか? 「腕の良いエンジニアに重要でない作業まで任せてしまっている」「腕の良いデザイナーに主業務以外も任せてしまっている」「すべての手が足りず細かいことまで手が回らない」などなど… 。

そんなときは、相談相手としてGaji-Laboにお気軽にお声がけください。あなたの開発チームに足りていない役割や領域を適切に捉えてカバーすることで、チーム全体の生産性と品質をアップさせるお手伝いをします。

オンラインでのヒアリングとフルリモートでのプロセス支援に対応していますので、リモートワーク対応可のパートナーをお探しの場合もぜひ弊社にお問い合わせください!

お悩み相談はこちらから!

投稿者 原田 直貴

受託と事業会社の両方を経験し、沢山の事業を見てみたい気持ちで Gaji-Labo を共同創業。普段は雑用やったりプロジェクトマネジメントやったり、たまにフロントエンドのコードを書いたり。直近は Gaji-Labo をデザイン会社に転換していく課題に挑戦中。期待値コントロールにステ全振り。