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.
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 the api
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:
export class HelloWorldService {
private apiUrl = '/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/`);
}
}
This request will then be received by your frontend angular-nginx
container and routed by nginx to your api
container using the following configuration block:
location /api/ {
proxy_pass http://api:8000; # Do not use the trailing slash here!
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;
}
As also noted here, it is common to define an environment variable (e.g., API_URL
) with a value like http://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:
import {environment} from '../../environments/environment';
...
export class HelloWorldService {
private apiUrl = environment.API_URL;
...
}