Lecciones aprendidas al implementar mi primera aplicación web de pila completa

Recientemente logré uno de mis objetivos a largo plazo: implementar mi primera aplicación web de pila completa.

En esta publicación, compartiré las lecciones que aprendí desde la perspectiva de un principiante, junto con algunos tutoriales útiles que seguí, obstáculos clave que tuve que superar y errores que cometí en el camino. Quiero ayudar a otros desarrolladores a comprender lo que implica la implementación de una aplicación web.

Después de pasar más de seis semanas buscando en Google, intentando, fallando y volviendo a intentarlo, finalmente logré implementar mi aplicación web. Estaba compuesto por un backend Node.js junto con un frontend React para una máquina virtual EC2 de Amazon Web Services (AWS).

Fue todo un desafío pero fue realmente satisfactorio, ya que al final la aplicación se implementó con éxito y ahora se puede acceder a ella a través de un nombre de dominio público.

La mayor dificultad para mí fue encontrar la información. No entendí lo que implicaba el despliegue. Así que luché por encontrar respuestas efectivas en la web. No pude encontrar una sola guía para todo el proceso.

Con suerte, puedo simplificar la curva de aprendizaje de implementación para la siguiente persona al reunir toda la información que aprendí en un solo lugar.

Así que aquí va ...

¿Qué significa implementar una aplicación?

Una aplicación web se divide en dos partes.

  • Código del lado del cliente: este es su código de interfaz de usuario de interfaz. Estos son archivos estáticos que no cambian durante la vida de su aplicación. Los archivos estáticos deben existir en algún lugar para que los usuarios puedan descargarlos y ejecutarlos en su navegador en el lado del cliente. Entraré en más detalles sobre dónde podría estar ese lugar más adelante.
  • Código del lado del servidor: se ocupa de toda la lógica de su aplicación. Debe ejecutarse en un servidor (máquina), comúnmente uno virtual como una instancia EC2, al igual que lo ejecuta cuando desarrolla localmente.

Para ejecutar su código local, el servidor debe tener una copia del mismo. Acabo de clonar mi repositorio de Github en el servidor desde la interfaz de línea de comandos del servidor.

También necesita configurar su servidor. Esto incluye:

  • configurar la máquina para poder acceder a Internet y ejecutar su código
  • exponer los puertos correctos
  • escuchando solicitudes HTTP (solicitudes de Internet)
  • apuntar un nombre de dominio personalizado al servidor desde el que se ejecuta su aplicación

Sabrá que está funcionando cuando pueda acceder a su aplicación usando su nombre de dominio personalizado desde cualquier máquina en Internet y toda la funcionalidad de su aplicación está funcionando como se esperaba.

Entonces, esa es una descripción general. Pero, ¿cómo lo hacemos realmente?

Empezando

Debería dividir su aplicación y desglosar el problema. Está implementando dos cosas diferentes: archivos estáticos del lado del cliente y código del lado del servidor.

Mi primer error fue pensar en mi aplicación como un todo, en lugar de dos aplicaciones separadas que se comunican entre sí.

Esto se sumó a la complejidad e hizo que buscar respuestas en Google fuera inútil. Me dejó abrumado.

Desglosé el problema en estos pasos. Aunque cada problema siempre se puede desglosar más.

  • Configurando su VM e implementando su Backend
  • Implementación de su frontend
  • Hacer que las dos aplicaciones se comuniquen
  • Señalar su nombre de dominio

En la siguiente figura, he intentado poner el proceso completo en un diagrama.

Configurando su VM e implementando su Backend

En mi caso, este fue un servidor Express.js implementado en una máquina virtual de Amazon EC2. Habría explicado cómo hacerlo, pero el tutorial "Creación y administración de un servidor Node.js en AWS - Parte 1" hace un trabajo mucho mejor.

Es el mejor tutorial que he encontrado en este espacio y cubre:

  • Inicio de una máquina virtual de AWS
  • Obtener los grupos de seguridad correctos para los puertos
  • Extrayendo código de GitHub a la máquina virtual
  • Ejecutando su servidor
  • Usando Nginx, un servidor HTTP, para reenviar solicitudes desde el puerto 80
  • Usando PM2 para persistir el proceso que ejecuta su servidor

Fue un salvavidas, y sin él probablemente todavía estaría estancado. Así que gracias, Robert Tod.

Puede probar fácilmente que su servidor se está ejecutando utilizando Postman para enviar una solicitud a uno de sus puntos finales Backend.

Implementación de su frontend

Entonces, ahora que tiene un servidor con su backend ejecutándose (espero), necesita que su Frontend funcione. Esto es realmente fácil cuando comprende el proceso.

Desafortunadamente, no lo hice durante mucho tiempo. Por ejemplo, al principio intenté ejecutar mi Frontend usando npm start.

Npm start crea un servidor de desarrollo local, sirviendo los archivos para que solo sean accesibles usando lo localhostque no es lo que queremos.

Para implementar el código Frontend, debe almacenar todos los archivos en su máquina virtual en una ubicación que su servidor web conoce. El servidor web permite a un cliente descargar el código y ejecutarlo en su navegador.

Apache y Nginx son ejemplos de servidores web.

Un servidor web escucha ciertos puertos, el puerto 80 o más comúnmente el puerto 443 (seguro), y sirve archivos estáticos (su código Frontend) o pasa la solicitud a un puerto diferente. Por ejemplo, vimos una solicitud al Backend en el tutorial de Node.js anterior.

Como el código Frontend es solo una colección de archivos almacenados en un servidor web, queremos hacer que estos archivos sean lo más pequeños y optimizados posible. Esto asegura que el cliente pueda descargarlos y ejecutarlos lo más rápido posible.

Las cargas de página más rápidas equivalen a usuarios felices.

Todos sus archivos JavaScript frontend se pueden agrupar en un solo archivo JavaScript. Esto generalmente se hace ejecutando npm run build, asumiendo que tiene este script definido en su package.json.

Puede leer más sobre el código de empaquetado aquí.

Basically, bundling your application removes anything that isn’t essential. This includes shortening names and placing all JavaScript code in one file. It will also compile your code into the correct JavaScript version. This is so all web browsers can understand and run it (for example, converting TypeScript to JavaScript).

When your code is bundled, you just have to copy the files into your web server. Then configure your web server to serve files stored at that location.

Here is a good article on deploying static files to an Nginx web server.

Hopefully, if all is going well (which it never does), your Frontend code is now working.

Visit the public DNS for the virtual machine to verify that the static information from the site loads.

Getting the Two Applications Communicating

So I had both my applications running individually, but something wasn’t right. I couldn’t get rid of a network request error.

This was the most frustrating point for me. I was so close, but I ran into some setbacks that ended up taking weeks to solve.

Cross-Origin Resource Sharing (CORS) is a mechanism that allows communication between different IP addresses or ports. You want your Backend to be allowed to send data back to your Frontend.

To enable this, your Frontend must include the correct headers when requesting resources. This can be done in two ways:

  • The headers can be added in Nginx although it takes some figuring out. You can start here.
  • You can use the cors npm module to include the headers.

A great way to test this if it is working is by looking within the network tab of your browser’s developer tools. This shows all the requests your application is making. If you select a request you can see where the request went to and what headers it included.

Once you have the right request headers being sent with your request, you have to make sure the requests are going to the correct place. This should be the address and port of your EC2 Backend server and not the address and port of your local Backend server like mine was.

Your Frontend communicates with your Backend using HTTP requests. Somewhere in your Frontend, code you will tell it where your Backend is located.

const networkInterface = createNetworkInterface({ uri: ‘//0.0.0.0:5000/graphql', });

Mine looked like this, which clearly was not going to be correct for my production server.

Annoyingly this made my application seem like it worked when I first navigated to it on my local machine, as my local server was running and able to return the required information.

To fix this, you can simply change the URI defined, but that means having to change it back every time you do further development, which is not the best approach (I know because I did it).

A more sophisticated solution is to include both URIs and use environment variables to select the appropriate one.

const networkInterface = createNetworkInterface({ uri: process.env.NODE_ENV === 'production' ? '//thecommunitymind.com/graphql' : '//0.0.0.0:5000/graphql', });

Simple but effective. Just make sure you set your NODE_ENV to production when using it for your production server.

We’re almost there. In fact, your deployment might work now.

But I had one last problem to overcome.

Even though my CORS setup was correct, the required headers were not being included consistently and were only getting added sometimes. For some POST requests, the CORS headers were not always present. Very odd!

This error lead me on a frustrating goose chase trying to fix my CORS setup in Nginx, when actually it had nothing to do with CORS.

Actually, I didn’t even need to do anything with CORS in Nginx, because I was using the CORS npm module.

The error was due to two other issues:

  • My database was included as an sqlite file in the Backend and
  • My process manager, PM2, was watching for file changes

So writing to the database file on a POST request caused PM2 to restart the server. This was leading to the correct headers not getting picked up which resulted in misleading errors.

A great tip and one I wish I had known earlier is to check your server logs on your EC2 instance. Whether you’re using PM2 or something else there will always be a way to check your logs. Just Google it!

These logs provided the key to solve my issue.

I simply had to turn off the watch ability of PM2. Bingo. And finally, it worked.

Pointing your Domain Name

This is the icing on the cake. You want a nice clean URL for your newly deployed application.

I bought my domain name through Amazon and used Route 53 to point it to the correct EC2 instance. This was a surprisingly painless experience.

Amazon’s tutorial was quite sufficient.

Summary

I hope this post has helped you understand the web application deployment process and ultimately get your amazing project online — whatever that may be.

At least you should have a better idea of what to Google for!

Good Luck.