Reputation: 29925
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"
Upvotes: 9
Views: 2637
Reputation: 287
As the accepted answer, I would've recommended you to use Runtime Configurations but I'm afraid it's been deprecated. Nevertheless, it's still possible to get the server runtime configurations "serverRuntimeConfig" value:
next.config.js
const nextConfig = {
serverRuntimeConfig: {
// Will only be available on the server side
apiUrl: process.env.NEXT_SERVER_API_URL || 'http://dockerbackend:8000'
},
publicRuntimeConfig: {
// Will be available on both server and client
apiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'
}
}
@/config/index.ts
import getConfig from 'next/config';
const config = getConfig();
let apiUrl = process.env.NEXT_PUBLIC_API_URL;
if (config) {
apiUrl = config?.serverRuntimeConfig?.apiUrl
}
export {
apiUrl
}
import { apiUrl } from '@/config'
Upvotes: 0
Reputation: 606
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`);
...
}
Upvotes: 9