I have reactjs app and express app dockerized.
This is my docker-compose.yml file:
version: "3"
services:
client:
image: react-app
build: ./client
ports:
- "3000:3000"
volumes:
- ./client/:/usr/src/app
- /usr/src/app/node_modules
container_name: mern-app-client
environment:
CHOKIDAR_USEPOLLING: "true"
server:
image: express-app
build: ./server
ports:
- "5000:5000"
volumes:
- ./server/:/usr/src/app
- /usr/src/app/node_modules
container_name: mern-app-server
environment:
CHOKIDAR_USEPOLLING: "true"
I have 2 dockerfiles, 1 in each client and server (built from node images).
After running docker compose up, I have server running at http://localhost:5000
and react app at http://localhost:3000
.
Everything is working fine when I have base_url as http://localhost:5000
in react app's axios/fetch
. In some blogs, not necessarily of mern, I saw localhost
being replaced with service name.
I tried the same and replaced the base_url
in react app with http://server:5000
but it didn't work.
Now I am confused in the core concept of when should we replace the base_url
and why is it needed?
I would be very grateful if somebody could explain.
Thanks for your time.
When you go to your browser and type http://localhost:3000
in, the browser makes a call to the client
container, does an HTTP GET to retrieve the Javascript code, and the browser actually runs the code. This is a critical difference: any fetch
calls or similar from your React application are running in the end user's browser, not inside Docker.
If a call is coming from the browser; it is part of your front-end application; or it is otherwise coming from outside Docker, then you need to use the host system's DNS name and the first published ports:
number. If the browser and the containers are both running on the same system you can use localhost
; if Docker is running in a VM (perhaps on the older Docker Toolbox setup) you need the VM's IP address.
+-------------+ +-----------------------+
| Browser | http://localhost:3000/ | client: |
| React app | ----------------------> | ports: ['3000:...'] |
+-------------+ +-----------------------+
It's also possible for one container to directly call each other. Maybe the client
container makes a call to server
as part of prerendering a page before it gets sent back to the browser; maybe server
has a backing db
. In this case you can use the Compose service name as a host name, and whatever port the server process is listening on. ports:
aren't required and are ignored if present.
+----------+ +-------------------------+
| server: | postgres://db:5432/ | db: |
| | -------------------> | # ports: ['...:5432'] |
+----------+ +-------------------------+
This setup is described further in Networking in Compose in the Docker documentation. You do not generally need to explicitly specify container_name:
or networks:
; Compose provides reasonable working defaults for these things for you.
For a browser-based application that needs to be configured with URLs for both the application itself and also a separate API server, a good practice is to set up a reverse proxy as a container. This could be something like Nginx, or in a pure development environment Webpack's proxy server. Since the proxy makes calls between two containers, you'd configure it with the Docker-internal URL http://server:5000
for the backend. This means that both the in-browser code and the backend APIs are on the same host and port, though, and so you can use a path-only relative URL /api/...
, bypassing the question of which host and port to use.