skip to Main Content

I am building a series of "Hello, World!" tutorials to edify myself and others on taking a simple (M)ERN app where a React frontend fetch’s a static string from an Express backend and then prints it to the screen (React: ‘Hello,’ Express: ‘ World!’). When run locally the App.js and the server.js work as expected but once I Dockerize them the and run Compose the frontend container cannot resolve the service name for the backend container. NOTE I am trying to do this locally and the nature of the error looks like CORS but I thought I handeled that configuration. Additionally I am using CRA and react-inject-env which based on the console.log is injecting correctly

App.js

import React, { useState, useEffect } from 'react';
import './App.css';
import { env } from './env' 
/**
 * App component: Fetches a greeting message from a backend API and displays it.
 *
 * Environment Variables:
 * - REACT_APP_BACKEND_URL: URL of the backend service.
 *    Defaults to "http://localhost:3000" for local development.
 *
 * @returns {JSX.Element} The rendered App component.
 */
function App() {
  // State to hold the greeting fetched from the backend
  const [greeting, setGreeting] = useState('');
 
  console.log(env.REACT_APP_BACKEND_URL);  

  useEffect(() => {
    /**
     * fetchGreeting function: Asynchronously fetches a greeting message from the backend.
     *
     * @async
     * @function
     */
    const fetchGreeting = async () => {
      try {
    const backendURL = env.REACT_APP_BACKEND_URL || "http://localhost:3000";
    //const backendURL = "http://localhost:3000";
        // Fetch the greeting from the backend
        const response = await fetch(`${backendURL}/`);
        const data = await response.text();
         
        // Update state with the fetched greeting
        setGreeting(data);
      } catch (error) {
        console.error("Failed to fetch the greeting: ", error);
      }
    };
 
    // Invoke the fetch function
    fetchGreeting();
  }, []); // Empty dependency array ensures this useEffect runs once on component mount.
 
  return (
    <div className="App">
      <header className="App-header">
        <p>Hello, {greeting}</p>
      </header>
    </div>
  );
}
 
export default App;

Frontend Dockerfile

type here
# Use an official node runtime as a parent image
FROM node:14
 
# Set the working directory in the container to /app
WORKDIR /app
 
# Copy package.json and package-lock.json to the container
COPY package*.json ./
 
# Install dependencies in the container
RUN npm install
 
# Install serve globally in the container
RUN npm install -g serve

# Bundle app source inside the docker image
COPY . .
 
# Build the app for production
RUN npm run build
 
# Indicate the port the app listens on
EXPOSE 3000

# Define the command to run the app
ENTRYPOINT npx react-inject-env set && serve -s build -l 3000

Server.js

/**
 * External Dependencies
 */
const express = require("express");
const cors = require("cors");

/**
 * Create a new Express application instance
 */
const app = express();

/**
 * Middleware
 */

// Use CORS middleware to allow requests from any origin for better cross-origin request support
console.log(process.env.CORS_ORIGIN);

const corsOptions = {
    origin: process.env.CORS_ORIGIN || "http://localhost:3001", // replace with your frontend application's URL
    optionsSuccessStatus: 200
};
app.use(cors(corsOptions));

/**
 * Routes
 */

// Endpoint to serve a simple greeting string
app.get("/", (req, res) => {
    res.send("World!");
});

/**
 * Server Activation
 */

// Start the Express server on port 3000
app.listen(3000, () => {
    console.log("Backend server listening on port 3000");
});

Backend Dockerfile

# Use an official node runtime as a parent image
FROM node:14
 
# Set the working directory in the container to /app
WORKDIR /app
 
# Copy package.json and package-lock.json to the container
COPY package*.json ./
 
# Install dependencies in the container
RUN npm install
 
# Bundle app source inside the docker image
COPY . .
 
# Define the network port that this container will listen on
EXPOSE 3000
 
# Define the command to run the app
CMD [ "node", "server.js" ]

docker-compose.yaml

version: "3" # We are using Docker v3 as v2 uses different syntax than that provided
services:
    frontend:
        build: ./frontend
        image: hello-world-frontend:latest
        ports:
            - "3001:3000" # Exposing frontend on port 3001
        environment:
            - REACT_APP_BACKEND_URL=http://backend:3000
        depends_on:
            - backend # Enforces the order with which the images will spin up
        networks:
            - internal
    backend:
        build: ./backend
        image: hello-world-backend:v1.0.0
        ports:
            - "3000:3000"
        environment:
            - CORS_ORIGIN=http://frontend:3001
        networks:
            - internal
networks:
    internal:
        driver: bridge

I have tried to lookup the network, Verify that all the names and forwarded ports line up, I am new to DevOps so I’m really not even sure what questions I need to be asking.

2

Answers


  1. Chosen as BEST ANSWER

    here is the Error Stack and the Network Error


  2. Your frontend app runs in your browser which isn’t on the bridge network. So when it needs to talk to the backend, it needs to go through the host.

    You only use the docker network service names as hostnames when the containers talk directly to each other. In your scenario, they don’t talk to each other. All the requests are coming from the browser.

    That means than you can’t use the backend hostname from the browser. You need to use the address of the host – and the mapped host port of the backend service.

    If you change

    REACT_APP_BACKEND_URL=http://backend:3000
    

    to

    REACT_APP_BACKEND_URL=http://localhost:3000
    

    it’ll work on your local machine.

    Likewise, your CORS origin will be http://localhost:3001.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search