I am trying to learn docker
. I have a next.js
app and a node.js
app.
In my next.js
app, in page.tsx I am calling my backend api
export const revalidate = 0;
export default async function Home() {
const response = await fetch("/api/getRandomNumber");
const data = await response.json();
console.log(data);
return (
<main className={styles.main}>
<h1>{data.lastInfo.randomNumber}</h1>
</main>
);
}
In nodejs
I have just created an endpoint getRandomNumber
with expressjs
router.get("/getRandomNumber", async (req, res) => {
const randNumber = `${Math.random() * 100}`;
const info = new Info({ randomNumber: randNumber });
await info.save();
const lastInfo = await Info.findOne({
where: { randomNumber: randNumber },
});
return res.status(200).send({ lastInfo });
});
I know next.js
is a full stack framework, but here I am trying to learn how to dockerize multiple services so created separate node.js
app
Below is my docker file for next.js app
FROM node:18-alpine
WORKDIR /usr/app
COPY ./package.json ./
RUN npm install
COPY ./ ./
CMD ["npm","run","dev"]
Below is my docker file for node.js
app
FROM node:18-alpine
WORKDIR /usr/app
COPY ./package.json ./
RUN npm install
COPY ./ ./
CMD ["npm","run","dev"]
Next I want to use nginx so I created a folder for it and inside it I created default.conf
and Dockerfile.dev
In default.conf
upstream nextapp {
server nextapp:3000;
}
upstream nodeapp {
server nodeapp:3001;
}
server {
listen 80;
location / {
proxy_pass http://nextapp;
}
location /api {
rewrite /api/(.*) /$1 break;
proxy_pass http://nodeapp;
}
location /ws {
proxy_pass http://nextapp;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
So I am appending /api to my nodejs app via default.conf
In Dockerfile.dev
FROM nginx
COPY ./default.conf /etc/nginx/conf.d/
Then one docker-compose.yml
file for next.js
, nginx
and node.js
version: "3"
services:
postgres:
image: "postgres:latest"
environment:
- POSTGRES_PASSWORD=postgres_password
nginx:
restart: always
build:
dockerfile: Dockerfile.dev
context: ./nginx
ports:
- "3050:80"
depends_on:
- nodeapp
- nextapp
nodeapp:
build:
dockerfile: Dockerfile.dev
context: ./nodeapp
volumes:
- /app/node_modules #don't try to override this folder
- ./nodeapp:/app
environment:
- PGUSER=postgres
- PGHOST=postgres
- PGDATABASE=postgres
- PGPASSWORD=postgres_password
- PGPORT=5432
nextapp:
build:
dockerfile: Dockerfile.dev
context: ./nextapp
volumes:
- /app/node_modules
- ./nextapp:/app
environment:
- WDS_SOCKET_PORT=0
I am able to see my node.js
app on http://localhost:3050/api/getRandomNumber
but in my next.js app I get error when I try to call the node.js api. If I comment out the fetch call in my next.js app it works fine on http://localhost:3050/
If I try to call http://localhost:3050/api/getRandomNumber
in fetch I get error saying Error: fetch failed
If I try to call just /api/getRandomNumber
in fetch I get error saying Failed to parse url
Github repo here
More screenshot
When you do fetch()
in NextJS server-side, you have to use the full URL.
So it should be something like fetch('http://localhost:3050/api/getRandomNumber')
.
By default compose creates a network and all the containers joins to that. But if you update your code to use localhost as base URL you will get an ECONNREFUSED error. It is because localhost
is not understoodable in context of docker - it points to the containers inner network.
So what docker can understand is the service name. If you put the service name there as base url, then it will work.
Here is the updated code:
const response = await fetch("http://nginx/api/getRandomNumber", { method: 'GET' });
Of course this is not something i'd go to production with. At least the base URL should be an env variable. And in production you wont run the containers in compose i think, normally those should be standalone EC2/S or Droplet instances.