Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Referencing Docker container from server-side (from another container) AND from client-side (browser) with same URL

I have two docker containers frontend and data-service.

frontend is using NextJS which is only relevant because NextJS has a method called getInitialProps() which can be run on the server, or can be run in the visitor's browser (I have no control over this).

In getInitialProps() I need to call an API to get the data for the page:

fetch('http://data-service:3001/user/123').then(...

When this is called on the server the API returns fine because my frontend container has access to the internal docker network and therefor can reference the data-service using the hostname http://data-service.

When this is called on the client, however, it fails (obviously) because Docker is now exposed as http://localhost and I can't reference http://data-service anymore.

How can I configure Docker so that I can use 1 URL for both use cases. I would prefer not to have to figure out which environment I'm in in my NextJS code if possible.

If seeing my docker-compose is useful I have included it below:

version: '2.2'
services:
  data-service:
    build: ./data-service
    command: npm run dev
    volumes:
      - ./data-service:/usr/src/app/
      - /usr/src/app/node_modules
    ports:
      - "3001:3001"
    environment:
      SDKKEY: "whatever"
  frontend:
    build: ./frontend
    command: npm run dev
    volumes:
      - ./frontend:/usr/src/app/
      - /usr/src/app/node_modules
    environment:
      API_PORT: "3000"
      API_HOST: "http://catalog-service"
    ports:
      - "3000:3000"
like image 573
Thomas Clayson Avatar asked Nov 28 '19 17:11

Thomas Clayson


People also ask

Can 2 containers communicate with each other?

If you are running more than one container, you can let your containers communicate with each other by attaching them to the same network. Docker creates virtual networks which let your containers talk to each other. In a network, a container has an IP address, and optionally a hostname.

What is the way to easily link a backend service Docker container with data only Docker container?

You need to connect them by placing both frontend and backend in same docker network (you don't need to put db in that network). Just create manually a standalone network and then reference it in docker-compose as external - same for backend and frontend.


1 Answers

The most elegant solution I've found is described in this post: Docker-compose make 2 microservices (frontend+backend) communicate to each other with http requests

Example implementation:

In next.config.js:

module.exports = {
  serverRuntimeConfig: {
    // Will only be available on the server side
    URI: 'your-docker-uri:port'
  },
  publicRuntimeConfig: {
    // Will be available on both server and client
    URI: 'http://localhost:port'
  }
}

In pages/index.js:

import getConfig from 'next/config';
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig();
const API_URI = serverRuntimeConfig.apiUrl || publicRuntimeConfig.apiUrl;

const Index = ({ json }) => <div>Index</div>;

Index.getInitialProps = async () => {
       ...
       const res = await fetch(`${API_URI}/endpoint`);
       ...
}
like image 180
struensee Avatar answered Nov 08 '22 01:11

struensee