Software Engineering

Getting Started Full Stack mit Amplify, AppSync und Angular

AWS bietet eine Vielzahl von Getting-Started Tutorials für die verschiedenen Services an. Doch wo fange ich bei einem kompletten Stack mit Amplify, AppSync und Angular an?

Hier will ich einen kompletten Getting Started Full Stack mit Amplify, AppSync, Angular, GraphQL und DynamoDB umsetzen. Außerdem möchte ich die Anwendung nicht nur auf dem localhost laufen lassen, sondern mithilfe von Amplify, S3 und CloudFront mit gültigem https Zertifikat bereitstellen. Diese Anwendung kann ich dann mit zusätzlichen Funktionen in Angular um Echtzeitsynchronisation erweitern oder zu einer PWA ausbauen (PWA benötigt https, das decke ich hier ab).

Die Anwendung ist für die Übersichtlichkeit bewusst minimalistisch gehalten. Sie besteht aus einem Request und einer Tabelle, welche die Daten darstellt. Mit dem Resultat kann die Anwendung aber leicht, um weitere CRUD-Operationen oder Subscriptions für die Echtzeit-Synchronisation erweitert werden. Das GraphQL Schema, Mutationen und Queries in diesem Beispiel werden alles Notwendige bereitstellen, nur die Angular Anwendung muss erweitert werden.

Technologien und Services

DynamoDB

Damit ich später das Schema in AppSync generieren kann, fange ich zuerst an die Datenbank einzurichten.

Hierfür gehe ich zuerst zum DynamoDB Service und erstelle eine Tabelle.

DynamoDB Tabelle für AppSync Import

 

Die Tabelle nenne ich „ToDos“ und als Primärschlüssel möchte ich die ID meiner Todos verwenden.

Tabelle für AppSync Import

Nach dem Erstellen der Tabelle landet man auf der Übersichtsseite. Diese enthält unter anderem auch die ARN (Amazon Resource Name), die ich noch später brauchen werde.

DynamoDB ARN für AppSync berechtigungen

Zuerst erstelle ich aber einen Testdatensatz, damit ich die Datenbank später auch testen kann. Dafür gehe ich auf den Tab „Elemente“ und anschließend auf „Element erstellen“. In der linken Ecke kann man die Darstellung auf Text setzen. Der Datensatz, den ich einfüge, besitzt folgenden Body:

Damit ist die Einrichtung der DynamoDB  abgeschlossen.

AWS IAM Rolle für AppSync

Damit AppSync Zugriff auf die DynamoDB bekommt, benötigt der Service zuerst eine Rolle mit den nötigen Berechtigungen.

Im Service AWS IAM kann der Wizard benutzt werden, um eine neue Rolle zu erstellen.

Ich möchte aber eine Rolle, die nur Zugriff auf diese eine Tabelle in DynamoDB hat. Dafür gehe ich in die mit dem Wizard neu erstellte Rolle unter Berechtigungsrichtlinien und füge eine neue Richtlinie mit folgendem JSON ein:

In „Resource“ muss die ARN der DynamoDB eingefügt werden. Die voreingestellte Richtlinie kann anschließend entfernt werden.

Stellt sicher, dass unter Vertrauensbeziehungen AppSync hinterlegt ist, ansonsten muss man das an dieser Stelle ergänzen.

AppSync API

Als nächstes kann ich die AppSync API erstellen.

AppSync API erstellen

Da ich bereits eine DynamoDB Tabelle erstellt habe kann ich sie jetzt einfach importieren, um das GraphQL Schema passend zu generieren.

DynamoDB-Tabelle importieren

Im nächsten Schritt kann ich die Datenbank-Tabelle und die zuvor erstellte IAM-Rolle auswählen.

Tabelle auswählen und Rolle zuweisen

Anschließend füge ich noch die anderen Attribute meiner Todos hinzu.

  • name: String
  • desc: String

Nach dem Erstellen ist der erste Meilenstein geschafft. Ich kann nun mit AWS AppSync Abfragen an die DynamoDB schicken.

AppSync Abfragen ausprobieren

Die Abfragen werden mit einem API Key durchgeführt. Mit diesem wird auch später das Frontend zugreifen. Wichtig hierbei ist, dass der API Key nach einer Woche abläuft und anschließend ein Neuer in den AppSync Einstellungen generiert werden muss. Für den produktiven Einsatz sollte man natürlich einer der anderen Authentifizierungsmöglichkeiten nutzen, aber für ein Getting-Started ist dieser Weg völlig ausreichend.

Angular Anwendung

Hier gehe ich darauf ein, wie die Anwendung modifiziert werden muss, aber nicht wie eine Angular Anwendung grundsätzlich erstellt wird. Den Ausgangscode gibt es in meinem Repo: https://github.com/Zwal/todoExampleStart.

Mit ng serve kann ich die Anwendung im localhost starten. Sie wurde lediglich mit ng new erzeugt und im app.component um eine Tabelle mit statischen Werten ergänzt.

Angular Anwendung

Zum Zeitpunkt des Artikels wird Angular 9 mit dem Ivy Compiler von Amplify nicht unterstützt. Deshalb muss ich die Version vom Angular zu einer älteren Version ändern.

Anschließend kann ich die Node Modules installieren und Kompatibilitätsprobleme beheben.

Sollte anschließend beim bauen der Fehler „An unhandled exception occurred: Job name „..getProjectMetadata“ does not exist.“ auftreten, muss noch die Version von „@angular-devkit/build-angular“ angepasst werden.

Jetzt sollte die Anwendung wieder auf dem localhost laufen.

AWS CLI Installieren

Die AWS CLI brauche ich, um local ein AWS-Profil zu hinterlegen. Mit diesem kann ich die AmplifyCLI einrichten.

Damit alle System abgedeckt sind, verweise ich hier auf die Installationsanleitung von AWS.

https://docs.aws.amazon.com/cli/latest/userguide/install-bundle.html

AWS CLI Benutzer anlegen

Nachdem ich die AWS CLI installiert habe, kann ich mir ein Profil anlegen. Dafür gehe ich in AWS in den IAM-Service. Dort lege ich mir einen neuen Benutzer an oder verwende einen Bestehenden. Mit diesem kann ich später von meinem Rechner Services anstoßen. Deshalb gebe ich dem Benutzer Administrationsrechte.

Unter dem Tab Sicherheitsanmeldeinformationen erstelle ich einen Zugriffsschlüssel.

IAM Benutzer für Amplify

Vom Zugriffsschlüssel brauche ich die Zugriffsschlüssel-ID und den geheimen Zugriffsschlüssel.

ID und geheimer Zugriffsschlüssel für Amplify Benutzer

Diese beiden Werte füge ich in das credentials Datei auf meinem Rechner ein.

Beim Mac befindet sie sich standardmäßig im folgenden Verzeichnis:

/Users/Ich/.aws/credentials

Das Verzeichnis ist versteckt und kann mit CMD+SHIFT+Punkt eingeblendet werden.

Die Datei sollte wie folgt aussehen:

Damit ist das AWS Profil eingerichtet und ich kann es mit Amplify nutzen.

AmplifyCLI Installieren

Als nächstes kann ich die AmplifyCLI, mithilfe von npm, installieren.

Danach kann ich Amplify initialisieren und konfigurieren.

Während der Initialisierung von Amplify wähle ich folgende Einstellungen für mein Projekt.

Ich nutze hier mein AWS Profil. Nachdem Bestätigen des AWS Profils ist die Initialisierung abgeschlossen.

Console nach erfolgreichem amplify init

Alle Einstellungen werden im Amplify Verzeichnis des Angular Projekts abgelegt und können nachträglich modifiziert werden.

amplify Ordnerstruktur in Angular

GraphQL Code generieren

Um mit der AmplifyCLI den GraphQL Code zu generieren, kopiere ich einfach den Befehl mit der api Id aus AppSync in mein Terminal. Gehe sicher das du dich beim Ausführen des Befehls im Root-Verzeichnis des Projektes befindest.

amplify codegen Befehl in AWS AppSync

Gegebenenfalls muss beim Ausführen des Befehls noch die Region ausgewählt werden, die restliche Punkte können alle bestätigt werden.

Unter dem src Verzeichnis befindet sich jetzt ein graphql Verzeichnis, mit dem Schema, Queries, Mutationen und Subscriptions, passend zur AppSync API. Zusätzlich dazu wurde noch die graphql config, aws-export.js (beinhaltet die AppSync API Informationen und den API-Key der nach einer Woche abläuft) und die API.service.ts generiert.

Damit die API richtig funktioniert muss ich noch an mehreren Stellen die Angular Anwendung anpassen.

In der polyfills.ts  setze ich die global Variable.

In der app.module.ts muss ich das HttpClientModule importieren und die generierte Klasse APIService (befindet sich in API.service.ts) als Provider definieren.

Die tsconfig.app.json braucht außerdem noch bei den Compiler Optionen den Typ node.

In der main.ts importiere ich die von Amplify generierte awsconfig.

Jetzt kann ich in der app.component.ts die statischen Todos entfernen und stattdessen den API.service nutzen, um über AppSync auf die DynamoDB zuzugreifen.

Nachdem ich ng serve ausführe bekomme ich nun die Todos, aus der DynamoDB, im Webbrowser dargestellt.

Die komplette Strecke der Webanwendung funktioniert jetzt. Die DynmoDB stellt die Daten bereit. AWS AppSync greift auf die DynamoDB zu und stellt eine Schnittstelle für den Zugriff bereit (welche auch Echtzeit Synchronisation anbietet). Die Angular Anwendung greift schließlich mit GraphQL Queries auf die Schnittstelle zu und stellt die bezogenen Daten dar.

Amplify Deployen/Releasen

Jetzt fehlt nur noch, dass meine Webanwendung öffentlich zugänglich und mit einem https Zertifikat versehen ist. Mit dem Zertifikat kann die Angular Anwendung auch zu einer PWA ausgebaut werden, da die Service-Worker einer PWA nur mit einem gültigen Zertifikat und https arbeiten.

Um meine Anwendung in der Amplify Cloud zu deployen, verwende ich folgenden Befehl im Terminal:

Als nächstes kann ich das Hosting meiner Webanwendung hinzufügen.

Nachdem ich den Befehl abgesetzt habe, kann ich noch ein paar Einstellungen vornehmen.

Um im nächsten Schritt einen Fehler über das fehlende dist Verzeichnis zu vermeiden, baue ich das Projekt zuerst.

Anschließend kann ich mit Amplify das Projekt auf CloudFront deployen.

Das Veröffentlichen der Anwendung dauert beim ersten Mal etwas länger, da AWS eine CloudFront Instanz erstellt. Nachdem alles erfolgreich erstellt und deployed ist, bekomme ich folgende Fehlermeldung für meine Mühe.

CloudFront AccessDenied Fehler

Vollautomatisiert schafft AWS anscheinend noch nicht die Webanwendung bei CloudFront bereitzustellen, um den Rest muss ich mich also selbst kümmern. An dem Fehler kann ich sehen das irgendwo auf der Strecke Berechtigungen fehlen. An DynamoDB oder AppSync kann es nicht liegen, da die Anwendung im localhost keine Probleme hat. Es kann also nur am von Amplify erstellten S3 Bucket oder CloudFront liegen.

Bugfix: Access-Denied

Um den Fehler zu beheben, gehe ich in AWS zu S3 und wähle mein S3-Bucket aus. Unter dem Tab Berechtigungen und Sub-Tab Bucket-Richtlinien füge ich den folgende Richtlinien ein.

So bekommt CloudFront zugriff auf das S3-Bucket.

Damit Enduser die Webseite einsehen können, erlaube ich bei der Zugriffskontrollliste öffentlichen Lesezugriff.

S3-Bucket Zugriffskontrolllieste

Bugfix: index.html nicht gefunden

Den Acess-Denied Fehler habe ich damit behoben. Allerdings bekomme ich anschließend einen neuen Fehler, denn die index.html kann nicht gefunden werden.

Dieser Fehler entsteht, wenn sich die index.html nicht im Root-Verzeichnis im S3-Bucket befindet. Meine index.html befindet sich im Ordner Frontend.

S3-Bucket root

Damit Angular das Projekt nicht in einen Unterordner baut, passe ich den output Pfad beim build in angular.json an.

Wenn ich jetzt das Projekt neu baue und mit Amplify veröffentliche, liegt die index.html im Root-Verzeichnis des S3-Buckets. Der CloudFront Link, welcher nach amplify publish in der Console ausgegeben wird, funktioniert jetzt und wirft keine Fehler mehr.

Angular Anwendung

Die Daten aus der DynamoDB werden in meiner Webanwendung angezeigt und sie hat ein gültiges Zertifikat. Yay!

Fazit

Ich konnte erfolgreich den Stack umsetzen. Zwar musste ich in einigen Einstellungen eingreifen, wie z.B. die S3-Richtlinien, aber der Aufwand hat sich in Grenzen gehalten. Amplify hat mir eine Menge Arbeit abgenommen, vor allem mit dem Generieren des GraphQL Codes und mit dem Hosten der Webanwendung. Möchte man seine eigene Webanwendung bereitstellen inkl. Datenbank in der Cloud, wird es kaum einen einfacheren Weg geben.

Wenn ein Bsp. gebraucht wird, wie die Funktionen für die Echtzeit Synchronisation aussehen und aufgerufen werden, kann ich die Chat-App von AWS empfehle.

https://github.com/aws-samples/aws-mobile-appsync-chat-starter-angular

Eine Stelle muss ich am ChatApp-Beispiel aber kritisieren. Die Entwickler halten sich nicht an den Standard von Amplify, dort werden die GraphQL Queries in Tags gepackt und mit TypeScript Klassen zurückgegeben. So verliert man z.B. die Syntaxhervorhebung und kann den von Amplify generierten Code nicht benutzen. Ich rate dringend davon ab, das so umzusetzen. Es ist weitaus praktischer den GraphQL Code von Amplify zu nutzen wie ich es in diesem Artikel beschrieben habe. So kann ich bei API Änderungen das Schema bei AppSync anpassen und alle Änderungen für das Frontend generieren.