Server- & Client-Side Networking with Multiple Docker Compose Files
How to Get a Docker Compose NextJS Service to Make Requests to an API Service from a Different Docker Compose
A few helpful articles have been written that discuss getting services from different Docker Compose files to communicate with one another during local development.
These solutions leverage Docker networking features, which makes sense since most of the use cases involve server-side network requests.
If the service is purely frontend, this isn't an issue at all. The browser would just use the local host machine's network to perform the requests. No need to go through Docker networking.
But NextJS makes things quite complicated. It is able to perform both server-side (SSR & SSG) and client-side (CSR) requests. It means that if we follow the articles above, server-side will work well, but client-side requests would break.
Allow me to demonstrate.
Let's say we have a NextJS app named next-client
in frontend.docker-compose.yml
. And we provide it with an environment variable API_URL=http://path-to-api
for backend requests.
# frontend.docker-compose.yml
services:
next-client:
environment:
API_URL: http://path-to-api
We also have an API service named api-gateway
exposing port 3000
in backend.docker-compose.yml
. That service will be available on the local machine network via localhost:3000. next-client
service's client-side (browser) requests will be totally fine with this (API_URL=http://localhost:3000
).
# backend.docker-compose.yml
services:
api-gateway:
expose:
- 3000
But due to reasons already mentioned by the articles above, all server-side next-client
requests will break.
Now, if we follow the articles, we would need to set API_URL=http://api-gateway:3000
in next-client
. Great, server-side requests are working now:
# backend.docker-compose.yml
services:
api-gateway:
expose:
- 3000
network:
- backend-gateway
networks:
- backend-gateway
# frontend.docker-compose.yml
services:
next-client:
environment:
API_URL: http://api-gateway:3000
networks:
- backend-project_api-gateway
networks:
backend-project_api-gateway:
external: true
But wait, the client-side requests are now failing! That totally makes sense since the local host machine doesn't recognize http://api-gateway
.
After fiddling around for hours, I found that the answer was actually so simple, add api-gateway
as a hostname alias inside your machine's hosts file. Here's how to do it in Linux:
127.0.0.1 localhost api-gateway
Voila! NextJS is happy!
What this does is to allow the host machine to route http://api-gateway
requests to all localhost network handlers, one of which is our Docker api-gateway
service! For client-side requests, http://api-gateway:3000
is transformed to http://127.0.0.1:3000
(localhost), and since api-gateway
is exposed at port 3000
, it accepts the request.
NextJS server-side requests, meanwhile, are still exclusively handled from within Docker's Network. The local machine network are not aware of them.
Effectively, in NextJS point of view, both server- & client-side requests go through the same network. But with some juggling, the reality is they are handled by separate networks, Docker and local machine networks, respectively. But NextJS doesn't need to know about that anymore, does it? 😎