Lehren aus der Bereitstellung meiner ersten Full-Stack-Webanwendung

Ich habe kürzlich eines meiner langfristigen Ziele erreicht: die Bereitstellung meiner ersten Full-Stack-Webanwendung.

In diesem Beitrag werde ich die Lektionen, die ich aus der Perspektive eines Anfängers gelernt habe, zusammen mit einigen nützlichen Tutorials, die ich befolgt habe, wichtigen Hindernissen, die ich überwinden musste, und Fehlern, die ich auf dem Weg gemacht habe, teilen. Ich möchte anderen Entwicklern helfen, zu verstehen, was bei der Bereitstellung einer Webanwendung erforderlich ist.

Nachdem ich mehr als sechs Wochen gegoogelt, versucht, versagt und erneut versucht hatte, gelang es mir schließlich, meine Webanwendung bereitzustellen. Es bestand aus einem Node.js-Backend und einem React-Frontend für eine virtuelle EC2-Maschine von Amazon Web Services (AWS).

Es war eine ziemliche Herausforderung, aber es war wirklich befriedigend, da die Anwendung am Ende erfolgreich bereitgestellt wurde und nun über einen gemeinfreien Namen zugänglich ist.

Die größte Schwierigkeit für mich war es, die Informationen zu finden. Ich habe nicht verstanden, worum es beim Einsatz ging. Deshalb hatte ich Mühe, im Web effektive Antworten zu finden. Ich konnte keine einzige Anleitung für den gesamten Prozess finden.

Hoffentlich kann ich die Bereitstellungslernkurve für die nächste Person vereinfachen, indem ich alle Informationen, die ich gelernt habe, an einem Ort zusammenbringe.

Also los geht's ...

Was bedeutet es, eine Anwendung bereitzustellen?

Eine Webanwendung ist in zwei Teile unterteilt.

  • Client-seitiger Code: Dies ist Ihr Frontend-UI-Code. Dies sind statische Dateien, die sich während der gesamten Lebensdauer Ihrer Anwendung nicht ändern. Statische Dateien müssen irgendwo vorhanden sein, damit Ihre Benutzer sie herunterladen und in ihrem Browser auf der Clientseite ausführen können. Ich werde später genauer darauf eingehen, wo das irgendwo sein könnte.
  • Serverseitiger Code: Hier wird die gesamte Logik Ihrer Anwendung behandelt. Es sollte auf einem Server (Computer) ausgeführt werden, normalerweise einem virtuellen Server wie einer EC2-Instanz, ähnlich wie Sie es bei der lokalen Entwicklung ausführen.

Um Ihren lokalen Code ausführen zu können, muss der Server über eine Kopie davon verfügen. Ich habe gerade mein Github-Repo über die Befehlszeilenschnittstelle des Servers auf den Server geklont.

Sie müssen auch Ihren Server einrichten. Das beinhaltet:

  • Richten Sie das Gerät so ein, dass Sie auf das Internet zugreifen und Ihren Code ausführen können
  • Freilegen der richtigen Ports
  • Abhören von HTTP-Anfragen (Internet-Anfragen)
  • Zeigen eines benutzerdefinierten Domänennamens auf den Server, von dem aus Ihre Anwendung ausgeführt wird

Sie werden wissen, dass es funktioniert, wenn Sie mit Ihrem benutzerdefinierten Domänennamen von jedem Computer im Internet auf Ihre Anwendung zugreifen können und alle Funktionen Ihrer Anwendung wie erwartet funktionieren.

Das ist also eine Übersicht. Aber wie machen wir das eigentlich?

Beginnen

Sie sollten Ihre Anwendung aufteilen und das Problem beheben. Sie stellen zwei verschiedene Dinge bereit: clientseitige statische Dateien und serverseitigen Code.

Mein erster Fehler war, meine Bewerbung als Ganzes zu betrachten und nicht zwei separate Bewerbungen, die miteinander kommunizieren.

Dies erhöhte die Komplexität und machte das Googeln nach Antworten nutzlos. Ich fühlte mich überwältigt.

Ich habe das Problem in diese Schritte unterteilt. Obwohl jedes Problem immer weiter aufgeschlüsselt werden kann.

  • Einrichten Ihrer VM und Bereitstellen Ihres Backends
  • Bereitstellen Ihres Frontends
  • Kommunikation der beiden Anwendungen
  • Zeigen Sie auf Ihren Domain-Namen

In der folgenden Abbildung habe ich versucht, den gesamten Prozess in ein Diagramm einzufügen.

Einrichten Ihrer VM und Bereitstellen Ihres Backends

In meinem Fall war dies ein Express.js-Server, der auf einer virtuellen Amazon EC2-Maschine bereitgestellt wurde. Ich hätte erklärt, wie es geht, aber das Tutorial „Erstellen und Verwalten eines Node.js-Servers unter AWS - Teil 1“ macht einen weitaus besseren Job.

Es ist das beste Tutorial, das mir in diesem Bereich begegnet ist und behandelt:

  • Starten einer virtuellen AWS-Maschine
  • Richtige Sicherheitsgruppen für Ports einrichten
  • Code von GitHub auf die virtuelle Maschine ziehen
  • Führen Sie Ihren Server aus
  • Verwenden von Nginx, einem HTTP-Server, zum Weiterleiten von Anforderungen von Port 80
  • Verwenden von PM2, um den Prozess fortzusetzen, auf dem Ihr Server ausgeführt wird

Es war ein Lebensretter, und ohne es würde ich wahrscheinlich immer noch stecken bleiben. Also danke, Robert Tod.

Sie können problemlos testen, ob Ihr Server mit Postman ausgeführt wird, um eine Anforderung an einen Ihrer Backend-Endpunkte zu senden.

Bereitstellen Ihres Frontends

Nachdem Sie nun einen Server mit ausgeführtem Backend haben (ich hoffe), müssen Sie Ihr Frontend zum Laufen bringen. Dies ist wirklich einfach, wenn Sie den Prozess verstehen.

Leider habe ich lange nicht. Zum Beispiel habe ich zu Beginn versucht, mein Frontend mit npm start auszuführen.

Npm start erstellt einen lokalen Entwicklungsserver, der die Dateien so bereitstellt, dass nur auf sie zugegriffen werden kann, localhostwas nicht das ist, was wir wollen.

Um den Frontend-Code bereitzustellen, müssen Sie alle Dateien auf Ihrer virtuellen Maschine an einem Ort speichern, den Ihr Webserver kennt. Über den Webserver kann ein Client den Code herunterladen und in seinem Browser ausführen.

Apache und Nginx sind Beispiele für Webserver.

Ein Webserver überwacht bestimmte Ports, Port 80 oder häufiger Port 443 (sicher), und stellt entweder statische Dateien (Ihren Frontend-Code) bereit oder leitet die Anforderung an einen anderen Port weiter. Zum Beispiel haben wir im obigen Node.js-Tutorial eine Anfrage an das Backend gesehen.

Da Frontend-Code nur eine Sammlung von Dateien ist, die auf einem Webserver gespeichert sind, möchten wir diese Dateien so klein und optimiert wie möglich gestalten. Dies stellt sicher, dass der Client sie so schnell wie möglich herunterladen und ausführen kann.

Schnellere Seite lädt gleich zufriedene Benutzer.

Alle Ihre Frontend-JavaScript-Dateien können in einer einzigen JavaScript-Datei gebündelt werden. Dies erfolgt normalerweise durch Ausführen von npm run build, vorausgesetzt, Sie haben dieses Skript in Ihrer package.json definiert.

Weitere Informationen zum Bündeln von Code finden Sie hier.

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.