Desplegar una aplicación Angular en producción

Estoy desarrollando una aplicación que consta de varios sistemas. No se trata para nada de algo hecho con microservicios, pero el sistema completo está compuesto de varias aplicaciones. Algunas del lado servidor, y una applicación web escrita en Angular.

En estas notas quiero apunta cómo desplegar una applicación Angular y servirla a través de un servidor HTTP nginx.

Primer intento

Existen varias formas de servir una aplicación Angular. La primera de ellas, es un servidor que viene incluido cuando creas tu aplicación con angular-cli. Muy fácil, simplemente tecleas el comando:

npm run serve

Y a correr.

Pero ese método, para producción, no es muy bueno. Para desarrollo sí, porque te detecta los cambios que vas haciendo en tu código y te recarga la aplicación en el navegador, por lo que automáticamente vas viendo tus cambios en el navegador.

Para desplegarla en producción hay que generar un paquete. En realidad hay que construir varios ficheros JavaScript y un pequeño fichero HTML que será el inicio de tu aplicación.

Ejecutamos el comando npm run build y estos ficheros se crearán en el directorio dist.

Copiando esos ficheros al directorio que sirve tu servidor web preferido (Apache, nginx,…), ya podrás acceder a tu aplicación web Angular.

Pero hay un problema, y es que mi aplicación usa rutas, lo que quiere decir es que la URL /devices/1234 en realidad no es una URL que obtiene HTML del servidor, si no que es Angular quien la reconoce y genera el HTML en el lado cliente. Así, si estás en esa ruta de la aplicación y refrescas la página, obtienes un error HTTP 404 como una catedral.

Tardé un tiempo en darme cuenta de ello, y en comprender cuál era el problema. Era cuestión de leer con más detalle la propia documentación de Angular…

Una mejor solución

Pues resulta que la solución se llama fallback to index.html (ver documentación).

If the app uses the Angular router, you must configure the server to return the application’s host page (index.html) when asked for a file that it does not have.

Esta gente de Angular es muy maja e incluye un ejemplo de configuración para nginx:

try_files $uri $uri/ /index.html;

Resulta que es un problema bastante conocido y en la documentación de ngnix lo encontrarás como Front Controller Pattern Web Apps.

¿Dónde hay que escribir esa línea de configuración?

Parece que funciona perfectamente en la sección server de /etc/nginx/nginx.conf:

# ...
server {
    listen 80;
    root /var/www/html;

    location /api/ {
        proxy_pass ...
    }

    # fallback to index.html
    try_files $uri $uri/ /index.html;
}

Referencias