SvelteKit SSR fetch() when backend is in a Docker container - node.js

I use docker compose for my project. It includes these containers:
Nginx
PostgreSQL
Backend (Node.js)
Frontend (SvelteKit)
I use SvelteKit's Load function to send request to my backend. In short, it sends http request to the backend container either on client-side or server-side. Which means, the request can be send not only by browser but also by container itself.
I can't get it to work on both client-side and server-side fetch. Only one of them is working.
I tried these URLs:
http://api.localhost/articles (only client-side request works)
http://api.host.docker.internal/articles (only server-side request works)
http://backend:8080/articles (only server-side request works)
I get this error:
From SvelteKit:
FetchError: request to http://api.localhost/articles failed, reason: connect ECONNREFUSED 127.0.0.1:80
From Nginx:
Timeout error
Docker-compose.yml file:
version: '3.8'
services:
webserver:
restart: unless-stopped
image: nginx:latest
ports:
- 80:80
- 443:443
depends_on:
- frontend
- backend
networks:
- webserver
volumes:
- ./webserver/nginx/conf/:/etc/nginx/conf.d/
- ./webserver/certbot/www:/var/www/certbot/:ro
- ./webserver/certbot/conf/:/etc/nginx/ssl/:ro
backend:
restart: unless-stopped
build:
context: ./backend
target: development
ports:
- 8080:8080
depends_on:
- db
networks:
- database
- webserver
volumes:
- ./backend:/app
frontend:
restart: unless-stopped
build:
context: ./frontend
target: development
ports:
- 3000:3000
depends_on:
- backend
networks:
- webserver
networks:
database:
driver: bridge
webserver:
driver: bridge
How can I send server-side request to docker container by using http://api.localhost/articles as URL? I also want my container to be accesible by other containers as http://backend:8080 if possible.

Use SvelteKit's externalFetch hook to have a different and overridden API URL in frontend and backend.
In docker-compose, the containers should be able to access each other by name if they are in the same Docker network.
Your frontend docker SSR should be able to call your backend docker by using the URL:
http://backend:8080
Web browser should be able to call your backend by using the URL:
(whatever reads in your Nginx configuration files)
Naturally, there are many reasons why this could fail. The best way to tackle this is to test URLs one by one, server by server using curl and entering addresses to the web browser address. It's not possible to answer the exact reason why it fails, because the question does not contain enough information, or generally repeatable recipe for the issue.
For further information, here is our sample configuration for a dockerised SvelteKit frontend. The internal backend shortcut is defined using hooks and configuration variables. Here is our externalFetch example.

From a docker compose you will be able to CURL from one container using the dns (service name you gave in the compose file)
CURL -XGET backend:8080
You can achieve this also by running all of these containers on host driver network.
Regarding the http://api.localhost/articles
You can change the /etc/hosts
And specify the IP you want your computer to try to communicate with when this url : http://api.localhost/articles is used.

Related

How to connect to Node API docker container from Angular Nginx container

I am currently working on an angular app using Rest API (Express, Nodejs) and Postgresql. Everything worked well when hosted on my local machine. After testing, I moved the images to Ubuntu server so the app can be hosted on an external port. I am able to access the angular frontend using the https://server-external-ip:80 but when trying to login, Nginx is not connecting to NodeApi. Here is my docker-compose file:
version: '3.0'
services:
db:
image: postgres:9.6-alpine
environment:
POSTGRES_DB: myDb
POSTGRES_PASSWORD: myPwd
ports:
- 5432:5432
restart: always
volumes:
- ./postgres-data:/var/lib/postgresql/data
networks:
- my-network
backend: # name of the second service
image: myId/mynodeapi
ports:
- 3000:3000
environment:
POSTGRES_DB: myDb
POSTGRES_PASSWORD: myPwd
POSTGRES_PORT: 5432
POSTGRES_HOST: db
depends_on:
- db
networks:
- my-network
command: bash -c "sleep 20 && node server.js"
myapp:
image: myId/myangularapp
ports:
- "80:80"
depends_on:
- backend
networks:
- my-network
networks:
my-network:
I am not sure what the apiUrl should be? I have tried the following and nothing worked:
apiUrl: "http://backend:3000/api"
apiUrl: "http://server-external-ip:3000/api"
apiUrl: "http://server-internal-ip:3000/api"
apiUrl: "http://localhost:3000/api"
I think you should use the docker-compose service as a DNS. It seems you've several docker hosts/ports available, there are the following in your docker-compose structure:
db:5432
http://backend:3000
http://myapp
Make sure to use db as POSTGRES_DB in the environment part for backend service.
Take a look to my repo, I think is the best way to learn how a similar project works and how to build several apps with nginx, you also can check my docker-compose.yml, it uses several services and are proxied using nginx and are worked together.
On this link you’ll find a nginx/default.conf file and it contains several nginx upstream configuration please take a look at how I used docker-compose service references there as hosts.
Inside the client/ directory, I also have another nginx as a web server of a react.js project.
On server/ directory, it has a Node.js API, It connects to Redis and Postgres SQL database also built from docker-compose.yml.
If you need set or redirect traffic to /api you can use some ngnix config like this
I think this use case can be useful for you and other users!

Dockeried setup for Node and Reactjs

I want to run two docker containers: one is node server(backend) and other has react js code(frontend).
My node contains an API as shown below:
app.get('/build', function (req, res) {
...
...
});
app.listen(3001);
console.log("Listening to PORT 3001");
I am using this API in my react code as follows:
componentDidMount() {
axios.get('http://localhost:3001/build', { headers: {"x-access-token": this.state.token}
})
.then(response => {
const builds = response.data.message;
//console.log("builds",builds);
this.setState({ builds: builds,done: true });
});
}
But when I run 2 different Docker containers, exposing 3001 for backend container and exposing 3000 for frontend container and access http://aws-ip:3000 (aws-ip is the public IP of my AWS instance where I am running the two docker containers), the request made is
http://localhost:3001/build due to which I am not able to hit the node api of docker container.
What changes should I make in the existing setup so that my react application can fetch the data from node server which is running on the same AWS instance?
You can follow his tutorial.
I think you can achieve that with docker-compose: https://docs.docker.com/compose/
Example: https://dev.to/numtostr/running-react-and-node-js-in-one-shot-with-docker-3o09
and here how I am using
version: '3'
services:
awsService:
build:
context: ./awsService
dockerfile: Dockerfile.dev
volumes:
- ./awsService/src:/app/src
ports:
- "3000:3000"
keymaster:
build:
context: ./keymaster
dockerfile: Dockerfile.dev
volumes:
- /app/node_modules
- ./keymaster:/app
ports:
- "8080:8080"
postgres:
image: postgres:12.1
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: mypassword
volumes:
- ./postgresql/data:/var/lib/postgresql/data
service:
build:
context: ./service
dockerfile: Dockerfile.dev
volumes:
- /app/node_modules
- ./service/config:/app/config
- ./service/src:/app/src
ports:
- "3001:3000"
ui:
build:
context: ./ui
dockerfile: Dockerfile.dev
volumes:
- /app/node_modules
- ./ui:/app
ports:
- "8081:8080"
For future reference, if you have something running on the same port, just bind your local machine port to a different port.
Hope this helps.
As you said it right, the frontend app accessed in the browser cannot reach your API via http://localhost:3001. Instead, your react application should access the API via http://[ec2-instance-elastic-ip]:3001. Your react app should store in its code. Your ec2 instance security group should allow incoming traffic via port 3001.
The above setup is enough to solve the problem.
but here are some additional tips.
assign an elastic IP to your instance. Otherwise, the public IP address of the instance will change if you stop/start the instance.
setup a domain name for your API for flexibility, easy to remember, can redeploy anywhere and point the domain name to the new address. (many ways to do this such as setting up a load balancer, set up CloudFront with ec2 origin)
setup SSL for the better security (many ways to do this such as setting up a load balancer, set up CloudFront with ec2 origin)
Since your react app is a static website, you can easily set up a static s3 website
Hope this helps.

Docker compose networking issue with react app

I am running a react app and a json server with docker-compose.
Usually I connect to the json server from my react app by the following:
fetch('localhost:8080/classes')
.then(response => response.json())
.then(classes => this.setState({classlist:classes}));
Here is my docker-compose file:
version: "3"
services:
frontend:
container_name: react_app
build:
context: ./client
dockerfile: Dockerfile
image: praventz/react_app
ports:
- "3000:3000"
volumes:
- ./client:/usr/src/app
backend:
container_name: json_server
build:
context: ./server
dockerfile: Dockerfile
image: praventz/json_server
ports:
- "8080:8080"
volumes:
- ./server:/usr/src/app
the problem is I can't seem to get my react app to fetch this information from the json server.
on my local machine I use 192.168.99.100:3000 to see my react app
and I use 192.168.99.100:8080 to see the json server but I can't seem to connect them with any of the following:
backend:8080/classes
json_server:8080/classes
backend/classes
json_server/classes
{host:"json_server/classes", port:8080}
{host:"backend/classes", port:8080}
Both the react app and the json server are running perfectly fine independently with docker-compose up.
What should I be putting in fetch() ?
Remember that the React application always runs in some user's browser; it has no idea that Docker is involved, and can't reach or use any of the Docker-related networking setup.
on my local machine I use [...] 192.168.99.100:8080 to see the json server
Then that's what you need in your React application too.
You might consider setting up some sort of proxy in front of this that can, for example, forward URL paths beginning with /api to the backend container and forward other URLs to the frontend container (or better still, run a tool like Webpack to compile your React application to static files and serve that directly). If you have that setup, then the React application can use a path /api/v1/... with no host, and it will be resolved relative to whatever the browser thinks "the current host" is, which should usually be the proxy.
You have two solutions:
use CORS on Express server see https://www.npmjs.com/package/cors
set up proxy/reverse proxy using NGINX

Docker: http requests from a container to other stalled

I use VPS for testing my web apps online. And I use Docker to run many web apps in the same server. Here is my
docker-compose.yml
version: "3.7"
services:
gateway:
build:
context: ./gateway
dockerfile: Dockerfile
restart: always
ports:
- 80:3000
networks:
erealm:
ipv4_address: 10.5.0.2
db:
image: mysql/mysql-server:5.5
restart: always
environment:
MYSQL_ROOT_PASSWORD: 4lf483t0
networks:
erealm:
ipv4_address: 10.5.0.3
phpmyadmin:
image: nazarpc/phpmyadmin:latest
environment:
- MYSQL_HOST=10.5.0.3:3306
restart: always
depends_on:
- db
ports:
- 1234:80
networks:
erealm:
ipv4_address: 10.5.0.4
static:
build:
context: ./static
dockerfile: Dockerfile
restart: always
networks:
erealm:
ipv4_address: 10.5.0.5
onlinecv:
build:
context: ./onlinecv
dockerfile: Dockerfile
restart: always
ports:
- 81:3000
networks:
erealm:
ipv4_address: 10.5.0.10
speeqapi:
build:
context: ./speeq/api
dockerfile: Dockerfile
restart: always
environment:
MYSQL_SERVER: 10.5.0.3
MYSQL_PORT: 3306
MYSQL_USER: xxxxxxxxxx
MYSQL_PASSWORD: xxxxxxxxxx
MYSQL_DATABASE: xxxxxxxxxx
depends_on:
- db
networks:
erealm:
ipv4_address: 10.5.0.20
speeqfe:
build:
context: ./speeq/fe
dockerfile: Dockerfile
restart: always
environment:
REACT_APP_API_SERVER: 10.5.0.20:3000
REACT_APP_STATIC_SERVER: 10.5.0.5:3000
ports:
- 82:3000
depends_on:
- db
- static
- speeqapi
networks:
erealm:
ipv4_address: 10.5.0.21
networks:
erealm:
driver: bridge
ipam:
config:
- subnet: 10.5.0.0/24
The main ideia behind this scheme is having only HTTP ports open to the world, while all necessary services run protected by Docker internal network, unaccesible to the world outside.
I use the gateway service to map the HTTP requests coming for the different apps to different ports. So, I have my online CV mapped to CNAME cv.eddealmeida.net and this Speeq app mapped to CNAME speeq.eddealmeida.net in my DNS zone, both pointing to this server. When my server receives a request to http://cv.eddealmeida.net or http://speeq.eddealmeida.net, the Node/Express-based gateway application (listening to port 80) splits the HOST paraments of the request an applies a simple mapping to send the requests to port 81 and 82 respectively.
Well, everything is running fine, but for the internal requests. First I had a problem with nternal name resolution, which I solved by giving IPs to all services, as you may see.
Now my internal requests are going to their correct places, but... the fetch requests made by the speeq frontend are stalling. They just keep stalling, over and over again. I tested the API using curl and everything is fine, it aswers correctly my command line requests. So, there is no problem with my API / Database connection or something like that. Google Chrome gave me this explanation, but I can't see me fitting in any of the cases mentioned.
Have someone ever lived a situation like this to give me a hint? I've been fighting this for the last 24 hours and run out of ideas. I double-checked everything and it still won't work.
I have few assumptions that might help.
1- Regarding the usage of IPs, I would suggest trying to use network aliases instead of IPs and this is a long-term solution
2- I can see that you are using ReactJS as a front-end which is a client side - I am assuming that you are using static files after building your React application - in this case you need to expose the backend/api to public ip through port mapping or using domain name points to a public ip where your api is listening or any similar method in order to make the front-end application able to reach it when you open it from the browser (which is a different device in your case). So if speeqfe is a reactjs frontend you need to change the environment variables value which points to the other containers to a public ip in order to make it work properly after building the static files

Docker connect NodeJS container with Apache container in front end JS

I am building a chat application that i am implementing in Docker. I have a NodeJS container with socket.io and a container with apache server and website on it.
The thing is i need to connect to the website(with javascript) to the NodeJS server. I have looked at the Docker-compose docks and read about networking. The docs said that the address should be the name of the container. But when i try that i get the following error in my browser console:
GET http://nodejs:3000/socket.io/socket.io.js net::ERR_NAME_NOT_RESOLVED
The whole project works outside containers.The only thing that i cannot figure out is the connection between the NodeJs container and the Apache container.
code that throws the error:
<script type="text/javascript" src="//nodejs:3000/socket.io/socket.io.js"></script>
My docker compose file:
version: '3.5'
services:
apache:
build:
context: ./
dockerfile: ./Dockerfile
networks:
default:
ports:
- 8080:80
volumes:
- ./:/var/www/html
container_name: apache
nodejs:
image: node:latest
working_dir: /home/node/app
networks:
default:
ports:
- '3001:3000'
volumes:
- './node_server/:/home/node/app'
command: [npm, start]
depends_on:
- mongodb
container_name: nodejs
networks:
default:
driver: bridge
Can anyone explain me how to succesfully connect the apache container to the NodeJS container so it can serve the socket.io.js file?
I can give more of the source code if needed.
The nodejs service is exposing port 3001 not 3000. 3001:3000 is a port mapping which forwards :3001 to the :3000 container port. So you would need to point it to nodejs:3001.
However, I don't think that'll work since the nodejs hostname is not accessible by the browser. You need to point it to the host in which docker is running since you are exposing those ports there. If you are running this locally it might look like:
<script type="text/javascript" src="//localhost:3001/socket.io/socket.io.js"></script>
In other words, you are not connecting to the nodejs server from the apache service, you are accessing it externally through the browser.

Resources