skip to Main Content

I have 2 simple node.js apps

app1/index.js

import fetch from 'node-fetch';
import express from 'express'
const app = express()
const port = 3000

app.get('/', (req, res) => {

    fetch("http://localhost:3002/", {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ "msg": "hello app2" })
    })
        .then(resp => resp.json())
        .then((resp) => {
            res.json(resp)
        })
})

app.listen(port, () => {
    console.log(`app1 listening on port ${port}`)
})

app2/index.js

const express = require('express')
const app = express()
const port = 3002

app.use(express.json());

app.post('/', (req, res) => {
    res.json({ "msg": "hello to you too" })
})

app.listen(port, () => {
    console.log(`app2 listening on port ${port}`)
})

when I go to localhost:3000 on my browser the app1 retrieves data from app2 and displays it and this works fine locally. The issue arises when I try to dockerize them

Dockerfile(s) for both

FROM node:18-alpine

WORKDIR /app

COPY . .

RUN npm i

EXPOSE 3000
# EXPOSE 3002 for app2


CMD ["node", "index.js"]

docker-compose.yaml

services:
  app1:
    build: ./app1
    container_name: app1_c
    ports:
      - "3000:3000"
    extra_hosts:
      - "host.docker.internal:host-gateway"

  app2:
    build: ./app2
    container_name: app2_c
    ports:
      - "3002:3002"
    extra_hosts:
      - "host.docker.internal:host-gateway"

This doesn’t work & the app1 can’t seem to access app2‘s localhost:3002
I’m guessing because app1 is sending the fetch request to it’s own container’s localhost:30002 instead of the app2’s localhost but I thought "host.docker.internal:host-gateway" fixes that issue and allows cross container communication?

Could someone please help me understand?

System Info
Ubuntu 22.04.3 LTS
Docker Engine v24.0.6

Edit: Changed names client -> app1 & server -> app2 to avoid confusion

2

Answers


  1. Chosen as BEST ANSWER

    Based on Phil's comment I've used environment variables and changed the app1 to as such:

    import fetch from 'node-fetch';
    import express from 'express'
    import 'dotenv/config'
    
    const app = express()
    const port = 3000
    
    app.get('/', (req, res) => {
    
        fetch(process.env.CONNECTION_URL, {
           method: 'POST',
           headers: { 'Content-Type': 'application/json' },
           body: JSON.stringify({ "msg": "hello app2" })
        })
            .then(resp => resp.json())
            .then((resp) => {
                res.json(resp)
            })
            .catch((err) => {
                res.json({ error: "could not reach app2" })
            })
    })
    
    app.listen(port, () => {
        console.log(`app1 listening on port ${port}`)
    })
    

    .env file contains localhost for local development:

    CONNECTION_URL="http://localhost:3002/"
    

    and in the docker-compose.yaml environment for production use:

    services:
      app1:
        build: ./app1
        container_name: app1_c
        ports:
          - "3000:3000"
        environment:
          - "CONNECTION_URL=http://app2:3002"
      
      app2:
        build: ./app2
        container_name: app2_c
        ports:
          - "3002:3002"
    

    This seems to work, but I'm leaving the question open for accepting another answer incase there's a better way to do this in general


  2. If you insist on having app1 and app2 to connect via localhost, you could start a container that runs processes for both apps, instead of using Docker Compose.

    In the root folder, place this Dockerfile

    FROM node:18-alpine
    
    WORKDIR /app
    
    COPY . .
    
    RUN cd ./app1 && npm i && cd -
    
    RUN cd ./app2 && npm i && cd -
    
    EXPOSE 3002
    EXPOSE 3000
    
    RUN ["chmod", "+x", "wrapper_script.sh"]
    CMD ./wrapper_script.sh
    

    and this wrapper_script.sh

    #!/bin/sh
    
    node app2/index.js & CONNECTION_URL="http://localhost:3002/" node app1/index.js
    
    wait -n
    
    exit $?
    

    And make sure to expose 2 ports during docker run

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