Server- & Client-Side Networking with Multiple Docker Compose Files

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

ยท

2 min read

Table of contents

No heading

No headings in the article.

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? ๐Ÿ˜Ž

ย