Next.jsのgetStaticProps、getServersidePropsでDocker環境のLaravel APIにアクセスする時のエラー対応のアレコレ
エラー概要
Dockerで構築したNext.js(SPA)+Laravel(API)でNext.jsのページコンポーネントでgetStaticProps
、getServersideProps
を以下のようにして使うとコネクションエラーになる。
コード(例)
export const getStaticProps = async () => {
const res = await fetch('http://localhost:8080/api/sample')
const json = res.json()
return {
props: {
data: json
}
}
}
エラー文
FetchError: request to http://localhost:8080/api/test failed, reason: connect ECONNREFUSED 127.0.0.1:8080
Docker環境
NginxをRPとしてNext.js、Laravelに処理を振っているわけではなく、localhost:3000
でNext.jsで受けて、localhost:8080
でNginx経由でLaravel APIにアクセスする構成。
(このエラーとは関係ないですが念の為)
設定ファイルの抜粋
nginx:
container_name: nginx
ports:
- 8080:80
原因
根本的な原因は不明だが
- クライアント側で使用するのはホスト側(localhost)のポート
- サーバーサイド側で使用するのはコンテナ側(docker)のポート
を使用しないといけないぽい。
getStaticProps
、getServersideProps
ともにサーバーサイドで実行されるのが関係していると思われる。。
解決方法
ローカル側のポートではなく、コンテナ側のポートを指定する(localhost→nginxコンテナのサービス名)
export const getStaticProps = async () => {
const res = await fetch('http://nginx:80/api/sample')
const json = res.json()
return {
props: {
data: json
}
}
}
なぜこの対応で解決できたのかが腑に落ちないので、調べたことを色々書いていこうと思う。
(React、Next.js、SSG、SSR、Node.jsについての理解がフワフワしていることは自覚している)
もしご存知の方はコメントで教えていただけると非常に嬉しいです!!
同じような内容がstackoverflowにあったけど誰もコメントしていない...
こっちで自己解決しているDiscussionがあった!
The issue was you can couldn't call docker host machine on a bridged network
訳「問題は、ブリッジされたネットワーク上でdockerホストマシンを呼び出すことができないことでした。」
こちらの記事も参考になる
公式リファレンスが情報源と書いていたけど、それっぽい記述が見つからない...
英語ドキュメントに記載があった
Each container can now look up the hostname web or db and get back the appropriate container’s IP address. For example, web’s application code could connect to the URL postgres://db:5432 and start using the Postgres database.
It is important to note the distinction between HOST_PORT and CONTAINER_PORT. In the above example, for db, the HOST_PORT is 8001 and the container port is 5432 (postgres default). Networked service-to-service communication uses the CONTAINER_PORT. When HOST_PORT is defined, the service is accessible outside the swarm as well.
Within the web container, your connection string to db would look like postgres://db:5432, and from the host machine, the connection string would look like postgres://{DOCKER_IP}:8001.
「各コンテナは、ホスト名 web または db を検索し、適切なコンテナの IP アドレスを返すことができます。例えば、webのアプリケーションコードは、postgres://db:5432というURLに接続し、Postgresデータベースの使用を開始することができます。
HOST_PORTとCONTAINER_PORTの違いに注意することが重要です。上記の例では、dbの場合、HOST_PORTは8001、コンテナポートは5432(postgresのデフォルト)となっています。ネットワーク上のサービス間通信では、CONTAINER_PORTを使用します。HOST_PORTが定義されていると、そのサービスはスウォームの外からもアクセス可能になります。
Webコンテナ内では、dbへの接続文字列はpostgres://db:5432のようになり、ホストマシンからの接続文字列はpostgres://{DOCKER_IP}:8001のようになります。」
Next.jsのgetServerSidePropsでLaravel APIで認証済みのユーザー情報を取得できない。
$user = Auth::user(); // null
原因不明😇
なのでgetServerSideProps(getStaticPropsも?)では認証中のユーザーに紐づくデータが取れないっぽいので、useEffectで取得するのがとりあえずの対処法...
getServerSidePropsで認証情報を適用する方法の調査結果
NextAuth.js
あとはコレとか
以下のファイルに記載されているコメントによると
「APIコールはサーバー上で実行されるため、デフォルトではブラウザに設定されているクッキーはありません。幸いなことに、reqオブジェクトからこれらのクッキーを抽出して、APIコールに添付することができます。」(日本語訳)
- As the API call is executed on the server it by
- default does not have the cookies set in the browser.
- Fortunately, we can extract these cookies from the req object
- and attach them to the api call.
const user = await axios.get("/api/user", {
headers: { Cookie: req.headers.cookie },
});
getServerSideProps
(getStaticProps
もかも)ではAPIで認証情報を判定できないので、
APIのルーティングに認証系のmiddlewareを適用すると認証状態だとしてもmiddlewareに引っかかってしまう。(=未認証と認識される)
上記コメントの通り、ヘッダーを指定すれば多分いける