Trying to deploy Django Rest Framework and Angular with Docker.
I have 3 containers: django, db and angular (with nginx on it)
The deployment is completed and I can open localhost:80 and see my angular components.
However, the component service is calling to http://localhost:8000/api/sensores/
to retrieve all the objects in the db (I made sure there is one object there, which I created from django shell, and reviewed it on the db container) and it is getting an error. Two logs appear on Chrome console:
Error al obtener sensores: ct {headers: e, status: 0, statusText: 'Unknown Error', url: 'http://localhost:8000/api/sensores/', ok: false, …}error: ProgressEvent {isTrusted: true, lengthComputable: false, loaded: 0, total: 0, type: 'error', …}headers: e {normalizedNames: Map(0), lazyUpdate: null, headers: Map(0)}message: "Http failure response for http://localhost:8000/api/sensores/: 0 Unknown Error"name: "HttpErrorResponse"ok: falsestatus: 0statusText: "Unknown Error"url: "http://localhost:8000/api/sensores/"[[Prototype]]: zn ...
polyfills-FFHMD2TL.js:1
GET http://localhost:8000/api/sensores/ net::ERR_CONNECTION_REFUSED
From exec console in my angular-nginx container, I can make this:
curl http://api:8000/api/sensores/
And get the object I’m looking for, but angular can’t do it itself.
Here my docker-compose.yml
:
version: '3.8'
services:
angular:
container_name: angular-nginx
build: ./hello
# context: ./hello
ports:
- "80:80"
volumes:
- ./hello/nginx.conf:/etc/nginx/nginx.conf:ro
- ./hello/dist/hello/browser:/usr/share/nginx/html
depends_on:
- db
api:
build:
context: ./django
command: gunicorn helloDocker.wsgi:application --bind 0.0.0.0:8000
volumes:
- ./django:/app
expose:
- "8000"
environment:
- DEBUG=False
- DJANGO_SECRET_KEY=your_secret_key
- DATABASE_URL=postgres://myuser:mypassword@localhost:5432/vituclim
depends_on:
- db
# Servicio de base de datos
db:
container_name: postgre
image: postgres:latest
environment:
POSTGRES_DB: vituclim
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
This is my nginx.conf, in angular project folder:
user nginx;
worker_processes auto;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://api:8000/;o
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
For the last config for /api/
directions, it doesn’t work with or without it.
I expect to call the component service and retrieve a list of objects (Sensor) to show on the HTML. The TS & HTML of the component are fine, as the problem comes from the service. This is the service component:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Sensor } from './sensor';
@Injectable({
providedIn: 'root'
})
export class HelloWorldService {
private nombreContenedorDjango = 'api'
private apiUrl = 'http://'+this.nombreContenedorDjango+':8000/api'; // URL del backend
constructor(private http: HttpClient) {}
getMessage(): Observable<{message: string}> {
return this.http.get<{message: string}>(`${this.apiUrl}/mensaje/`);
}
getSensores(): Observable<Sensor[]> {
return this.http.get<any>(`${this.apiUrl}/sensores/`);
}
}
I suppose the problem comes from angular, or nginx configuration. But I can’t see it clearly.
2
Answers
The component service should call http://localhost:80/api/sensores/
Nginx is running on
angular-nginx
container in which you opened and mapped the port 80.As per your nginx config, it will handle the redirect to your server and will be able to do that because you exposed the port 8000 (in
api
service) to other docker containers (and this is why you can curl http://api:8000/api/sensores/ inside nginx container).Quick and simple explanation to anyone who’s wondering:
ports
config opens a port on "your machine" and maps it to a port in the container you are configuring. In this case, for example, it opens the port 80 and maps it to the port 80 of the container angular-nginx.expose
config, instead, opens a port on the container you are configuring, but it will be reachable from another container only.I have already answered your question in the comments, but unfortunately, it seems you did not understand what I was trying to explain.
Your Angular code, once built, is executed in the user’s browser’s JavaScript VM. When you make a request to
http://localhost/api/sensores/
from your JavaScript code, you are effectively pointing to the user’s computer. Naturally, there is no server running there to respond.When you make a request to
http://api:8000/api/sensores/
, you are asking the user’s browser to resolve theapi
domain name. However, this domain is resolvable only via Docker’s internal name resolution system, which is obviously won’t be used by the user’s browser/OS.Instead, you should make a relative request like
/api/sensores/
(as already mentioned here). The user’s browser will automatically use the current web page’s domain (or IP address) and request scheme (HTTP/HTTPS) to form the full URL:This request will then be received by your frontend
angular-nginx
container and routed by nginx to yourapi
container using the following configuration block:As also noted here, it is common to define an environment variable (e.g.,
API_URL
) with a value likehttp://localhost:8000/api/
for development and/api/
for production (see this answer for the example). You can then use it in your code as follows: