Hinzufügen dynamischer und asynchroner Funktionen zu JAMstack-Sites
Veröffentlicht: 2022-03-10Bedeutet das, dass JAMstack-Sites keine dynamischen Interaktionen verarbeiten können? Definitiv nicht!
JAMstack-Sites eignen sich hervorragend zum Erstellen hochdynamischer, asynchroner Interaktionen. Mit einigen kleinen Anpassungen, wie wir über unseren Code denken, können wir unterhaltsame, immersive Interaktionen erstellen, indem wir nur statische Assets verwenden!
Es kommt immer häufiger vor, dass Websites mit dem JAMstack erstellt wurden – das heißt, Websites, die als statische HTML-Dateien bereitgestellt werden können, die aus JavaScript, Markup und APIs erstellt wurden. Unternehmen lieben den JAMstack, weil er die Infrastrukturkosten senkt, die Bereitstellung beschleunigt und die Hürden für Leistungs- und Sicherheitsverbesserungen senkt, da durch den Versand statischer Assets die Notwendigkeit entfällt, Server zu skalieren oder Datenbanken hochverfügbar zu halten (was auch bedeutet, dass es keine Server oder Datenbanken gibt, die dies können gehackt werden). Entwickler mögen den JAMstack, weil er die Komplexität reduziert, eine Website live im Internet zu bringen: Es müssen keine Server verwaltet oder bereitgestellt werden; wir können Front-End-Code schreiben und er geht einfach wie von Zauberhand live .
(„Magic“ sind in diesem Fall automatisierte statische Bereitstellungen, die kostenlos von einer Reihe von Unternehmen erhältlich sind, einschließlich Netlify, wo ich arbeite.)
Aber wenn Sie viel Zeit damit verbringen, mit Entwicklern über den JAMstack zu sprechen, stellt sich die Frage, ob der JAMstack mit Serious Web Applications umgehen kann oder nicht. Schließlich sind JAMstack-Sites statische Sites, richtig? Und sind statische Seiten nicht sehr eingeschränkt in ihren Möglichkeiten?
Dies ist ein weit verbreitetes Missverständnis, und in diesem Artikel werden wir untersuchen, woher das Missverständnis kommt, die Fähigkeiten des JAMstacks betrachten und einige Beispiele für die Verwendung des JAMstacks zum Erstellen seriöser Webanwendungen durchgehen.
JAMstack-Grundlagen
Phil Hawksworth erklärt, was JAMStack eigentlich bedeutet und wann es sinnvoll ist, es in Ihren Projekten zu verwenden, sowie wie es sich auf Tools und Frontend-Architektur auswirkt. Lesen Sie einen verwandten Artikel →
Was macht eine JAMstack-Site „statisch“?
Webbrowser laden heute HTML-, CSS- und JavaScript-Dateien, genau wie damals in den 90er Jahren.
Eine JAMstack-Site ist im Kern ein Ordner voller HTML-, CSS- und JavaScript-Dateien.
Dies sind „statische Assets“, was bedeutet, dass wir keinen Zwischenschritt benötigen, um sie zu generieren (zum Beispiel benötigen PHP-Projekte wie WordPress einen Server, um das HTML bei jeder Anfrage zu generieren ).
Das ist die wahre Stärke des JAMstack: Er benötigt keine spezialisierte Infrastruktur, um zu funktionieren. Sie können eine JAMstack-Site auf Ihrem lokalen Computer ausführen, indem Sie sie in Ihr bevorzugtes Content Delivery Network (CDN) stellen und sie mit Diensten wie GitHub Pages hosten – Sie können den Ordner sogar per Drag-and-Drop in Ihren bevorzugten FTP-Client hochladen, um ihn hochzuladen zum Shared-Hosting.
Statische Assets bedeuten nicht unbedingt statische Erlebnisse
Da JAMstack-Sites aus statischen Dateien bestehen, ist es leicht anzunehmen, dass die Erfahrung auf diesen Sites, wissen Sie, statisch ist. Aber das ist nicht der Fall!
JavaScript ist in der Lage, eine ganze Menge dynamischer Dinge zu tun. Schließlich sind moderne JavaScript-Frameworks statische Dateien, nachdem wir den Build-Schritt durchlaufen haben – und es gibt Hunderte von Beispielen für unglaublich dynamische Website-Erlebnisse, die von ihnen angetrieben werden.
Es gibt ein weit verbreitetes Missverständnis, dass „statisch“ unflexibel oder fixiert bedeutet. Aber alles, was „statisch“ im Zusammenhang mit „statischen Sites“ wirklich bedeutet, ist, dass Browser keine Hilfe benötigen, um ihre Inhalte bereitzustellen – sie können sie nativ verwenden, ohne dass ein Server zuerst einen Verarbeitungsschritt durchführt.
Oder anders ausgedrückt:
„Statische Assets“ bedeutet nicht statische Apps; es bedeutet, dass kein Server erforderlich ist.
„
Kann der JAMstack das?
Wenn jemand nach dem Erstellen einer neuen App fragt, werden häufig Vorschläge für JAMstack-Ansätze wie Gatsby, Eleventy, Nuxt und andere ähnliche Tools angezeigt. Ebenso häufig kommt es zu Einwänden: „Statische Site-Generatoren können _______ nicht“, wobei _______ etwas Dynamisches ist.
Aber – wie wir im vorherigen Abschnitt angesprochen haben – JAMstack-Sites können mit dynamischen Inhalten und Interaktionen umgehen!
Hier ist eine unvollständige Liste von Dingen, von denen ich wiederholt gehört habe, dass Leute behaupten, der JAMstack könne nicht damit umgehen, was er definitiv kann:
- Daten asynchron laden
- Behandeln Sie Verarbeitungsdateien, z. B. das Bearbeiten von Bildern
- Lesen und Schreiben in eine Datenbank
- Behandeln Sie die Benutzerauthentifizierung und schützen Sie Inhalte hinter einer Anmeldung
In den folgenden Abschnitten sehen wir uns an, wie jeder dieser Workflows auf einer JAMstack-Site implementiert wird.
Wenn Sie es kaum erwarten können, den dynamischen JAMstack in Aktion zu sehen, können Sie sich zuerst die Demos ansehen und dann zurückkommen, um zu erfahren, wie sie funktionieren.
Hinweis zu den Demos :
Diese Demos sind ohne Frameworks geschrieben. Sie sind nur HTML, CSS und Standard-JavaScript. Sie wurden für moderne Browser (z. B. Chrome, Firefox, Safari, Edge) entwickelt und nutzen neuere Funktionen wie JavaScript-Module, HTML-Vorlagen und die Fetch-API. Es wurden keine Polyfills hinzugefügt. Wenn Sie also einen nicht unterstützten Browser verwenden, werden die Demos wahrscheinlich fehlschlagen.
Laden Sie Daten asynchron von einer Drittanbieter-API
„Was ist, wenn ich neue Daten abrufen muss, nachdem meine statischen Dateien erstellt wurden?“
Im JAMstack können wir zahlreiche asynchrone Anforderungsbibliotheken nutzen, einschließlich der integrierten Fetch-API, um Daten jederzeit mit JavaScript zu laden.
Demo: Suchen Sie eine Drittanbieter-API von einer JAMstack-Site
Ein häufiges Szenario, das asynchrones Laden erfordert, ist, wenn der benötigte Inhalt von Benutzereingaben abhängt. Wenn wir beispielsweise eine Suchseite für die Rick & Morty-API erstellen, wissen wir nicht, welche Inhalte angezeigt werden sollen, bis jemand einen Suchbegriff eingegeben hat.
Um damit umzugehen, müssen wir:
- Erstellen Sie ein Formular, in das Benutzer ihren Suchbegriff eingeben können,
- Hören Sie auf eine Formularübermittlung,
- Holen Sie sich den Suchbegriff aus der Formularübermittlung,
- Senden Sie eine asynchrone Anfrage mit dem Suchbegriff an die Rick & Morty-API.
- Zeigen Sie die Anfrageergebnisse auf der Seite an.
Zuerst müssen wir ein Formular und ein leeres Element erstellen, das unsere Suchergebnisse enthält, das so aussieht:
<form> <label for="name">Find characters by name</label> <input type="text" name="name" required /> <button type="submit">Search</button> </form> <ul></ul>
Als Nächstes müssen wir eine Funktion schreiben, die das Senden von Formularen verarbeitet. Diese Funktion wird:
- Verhindern Sie das standardmäßige Formularübermittlungsverhalten
- Holen Sie sich den Suchbegriff aus der Formulareingabe
- Verwenden Sie die Fetch-API, um mit dem Suchbegriff eine Anfrage an die Rick & Morty-API zu senden
- Rufen Sie eine Hilfsfunktion auf, die die Suchergebnisse auf der Seite anzeigt
Wir müssen dem Formular auch einen Ereignis-Listener für das Submit-Ereignis hinzufügen, das unsere Handler-Funktion aufruft.
So sieht dieser Code insgesamt aus:
<script type="module"> import showResults from './show-results.js'; const form = document.querySelector('form'); const handleSubmit = async event => { event.preventDefault(); // get the search term from the form input const name = form.elements['name'].value; // send a request to the Rick & Morty API based on the user input const characters = await fetch( `https://rickandmortyapi.com/api/character/?name=${name}`, ) .then(response => response.json()) .catch(error => console.error(error)); // add the search results to the DOM showResults(characters.results); }; form.addEventListener('submit', handleSubmit); </script>
Hinweis: Um sich auf dynamisches JAMstack-Verhalten zu konzentrieren, werden wir nicht diskutieren, wie Hilfsfunktionen wie showResults geschrieben werden. Der Code ist jedoch ausführlich kommentiert, also schauen Sie sich die Quelle an, um zu erfahren, wie er funktioniert!
Mit diesem Code können wir unsere Website in einem Browser laden und sehen das leere Formular ohne Ergebnisse:
Wenn wir einen Charakternamen eingeben (z. B. „Rick“) und auf „Suchen“ klicken, sehen wir eine Liste von Charakteren, deren Namen „Rick“ enthalten, angezeigt:
Hey! Hat diese statische Seite gerade Daten dynamisch geladen? Heilige Eimer!
Sie können dies in der Live-Demo selbst ausprobieren oder sich den vollständigen Quellcode für weitere Details ansehen.
Bewältigen Sie teure Rechenaufgaben vom Gerät des Benutzers
In vielen Apps müssen wir Dinge tun, die ziemlich ressourcenintensiv sind, wie z. B. die Verarbeitung eines Bildes. Während einige dieser Arten von Vorgängen nur mit clientseitigem JavaScript möglich sind, ist es nicht unbedingt eine gute Idee, die Geräte Ihrer Benutzer all diese Arbeit erledigen zu lassen. Wenn sie ein Gerät mit geringem Stromverbrauch verwenden oder versuchen, ihre letzten 5 % der Akkulaufzeit auszudehnen, wird es für sie wahrscheinlich eine frustrierende Erfahrung sein, ihr Gerät einen Haufen Arbeit erledigen zu lassen.
Bedeutet das also, dass JAMstack-Apps kein Glück haben? Ganz und gar nicht!
Das „A“ in JAMstack steht für APIs. Das bedeutet, dass wir diese Arbeit an eine API senden können und vermeiden, dass die Computerlüfter unserer Benutzer auf die „Hover“-Einstellung hochgefahren werden.
„Aber warte“, könntest du sagen. „Wenn unsere App benutzerdefinierte Arbeiten ausführen muss und diese Arbeit eine API erfordert, bedeutet das nicht, dass wir einen Server bauen?“
Dank der Leistungsfähigkeit serverloser Funktionen müssen wir das nicht!
Serverlose Funktionen (auch „Lambda-Funktionen“ genannt) sind eine Art API, für die keine Serverbausteine erforderlich sind. Wir müssen eine einfache alte JavaScript-Funktion schreiben, und die gesamte Arbeit der Bereitstellung, Skalierung, des Routings usw. wird an unseren bevorzugten serverlosen Anbieter ausgelagert.
Die Verwendung serverloser Funktionen bedeutet nicht, dass es keinen Server gibt; es bedeutet nur, dass wir nicht an einen Server denken müssen.
„
Serverlose Funktionen sind die Erdnussbutter für unseren JAMstack: Sie erschließen eine ganze Welt leistungsstarker, dynamischer Funktionen, ohne dass wir uns jemals mit Servercode oder Entwicklern befassen müssen.
Demo: Konvertieren Sie ein Bild in Graustufen
Nehmen wir an, wir haben eine App, die Folgendes tun muss:
- Laden Sie ein Bild von einer URL herunter
- Konvertieren Sie dieses Bild in Graustufen
- Laden Sie das konvertierte Bild in ein GitHub-Repository hoch
Soweit ich weiß, gibt es keine Möglichkeit, solche Bildkonvertierungen vollständig im Browser durchzuführen – und selbst wenn, wäre es eine ziemlich ressourcenintensive Sache, also wollen wir unsere Benutzer wahrscheinlich nicht so belasten ' Geräte.
Stattdessen können wir die zu konvertierende URL an eine serverlose Funktion senden, die uns die schwere Arbeit abnimmt und eine URL zu einem konvertierten Bild zurücksendet.
Für unsere serverlose Funktion verwenden wir Netlify Functions. Im Code unserer Website fügen wir auf der Stammebene einen Ordner namens „functions“ hinzu und erstellen darin eine neue Datei namens „convert-image.js“. Dann schreiben wir einen sogenannten Handler, der Anfragen an unsere serverlose Funktion empfängt und – wie Sie vielleicht erraten haben – verarbeitet .
Um ein Bild zu konvertieren, sieht es so aus:
exports.handler = async event => { // only try to handle POST requests if (event.httpMethod !== 'POST') { return { statusCode: 404, body: '404 Not Found' }; } try { // get the image URL from the POST submission const { imageURL } = JSON.parse(event.body); // use a temporary directory to avoid intermediate file cruft // see https://www.npmjs.com/package/tmp const tmpDir = tmp.dirSync(); const convertedPath = await convertToGrayscale(imageURL, tmpDir); // upload the processed image to GitHub const response = await uploadToGitHub(convertedPath, tmpDir.name); return { statusCode: 200, body: JSON.stringify({ url: response.data.content.download_url, }), }; } catch (error) { return { statusCode: 500, body: JSON.stringify(error.message), }; } };
Diese Funktion bewirkt Folgendes:
- Überprüft, ob die Anfrage mit der HTTP POST-Methode gesendet wurde
- Ruft die Bild-URL aus dem POST-Body ab
- Erstellt ein temporäres Verzeichnis zum Speichern von Dateien, die bereinigt werden, sobald die Funktion ausgeführt wird
- Ruft eine Hilfsfunktion auf, die das Bild in Graustufen konvertiert
- Ruft eine Hilfsfunktion auf, die das konvertierte Bild auf GitHub hochlädt
- Gibt ein Antwortobjekt mit einem HTTP 200-Statuscode und der URL des neu hochgeladenen Bildes zurück
Hinweis : Wir werden nicht darauf eingehen, wie die Hilfsfunktionen für die Bildkonvertierung oder das Hochladen auf GitHub funktionieren, aber der Quellcode ist gut kommentiert, damit Sie sehen können, wie er funktioniert.
Als nächstes müssen wir ein Formular hinzufügen, das verwendet wird, um URLs zur Verarbeitung einzureichen, und einen Ort, an dem das Vorher und Nachher angezeigt wird:
<form action="/.netlify/functions/convert-image" method="POST" > <label for="imageURL">URL of an image to convert</label> <input type="url" name="imageURL" required /> <button type="submit">Convert</button> </form> <div></div>
Schließlich müssen wir dem Formular einen Ereignis-Listener hinzufügen, damit wir die URLs zur Verarbeitung an unsere serverlose Funktion senden können:
<script type="module"> import showResults from './show-results.js'; const form = document.querySelector('form'); form.addEventListener('submit', event => { event.preventDefault(); // get the image URL from the form const imageURL = form.elements['imageURL'].value; // send the image off for processing const promise = fetch('/.netlify/functions/convert-image', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ imageURL }), }) .then(result => result.json()) .catch(error => console.error(error)); // do the work to show the result on the page showResults(imageURL, promise); }); </script>
Nachdem wir die Site (zusammen mit ihrem neuen Ordner „functions“) für Netlify bereitgestellt und/oder Netlify Dev in unserer CLI gestartet haben, können wir das Formular in unserem Browser sehen:
Wenn wir dem Formular eine Bild-URL hinzufügen und auf „Konvertieren“ klicken, sehen wir während der Konvertierung für einen Moment „Verarbeitung…“, dann sehen wir das Originalbild und sein neu erstelltes Gegenstück in Graustufen:
Oh Mist! Unsere JAMstack-Site hat gerade ein ziemlich ernstes Geschäft abgewickelt, und wir mussten nicht ein einziges Mal über Server nachdenken oder die Batterien unserer Benutzer entladen!
Verwenden Sie eine Datenbank zum Speichern und Abrufen von Einträgen
In vielen Apps benötigen wir zwangsläufig die Möglichkeit, Benutzereingaben zu speichern. Und das bedeutet, dass wir eine Datenbank brauchen.
Sie denken vielleicht: „Das ist es also, richtig? Das Spiel ist aus? Sicherlich kann eine JAMstack-Site – von der Sie uns gesagt haben, dass sie nur eine Sammlung von Dateien in einem Ordner ist – nicht mit einer Datenbank verbunden werden!“
Im Gegenteil.
Wie wir im vorherigen Abschnitt gesehen haben, geben uns serverlose Funktionen die Möglichkeit, alle möglichen leistungsstarken Dinge zu tun, ohne unsere eigenen Server erstellen zu müssen.
Ebenso können wir Database-as-a-Service (DBaaS)-Tools (wie Fauna) verwenden, um eine Datenbank zu lesen und zu schreiben, ohne selbst eine einrichten oder hosten zu müssen.
DBaaS-Tools vereinfachen den Prozess der Einrichtung von Datenbanken für Websites erheblich: Das Erstellen einer neuen Datenbank ist so einfach wie das Definieren der Datentypen, die wir speichern möchten. Die Tools generieren automatisch den gesamten Code zum Verwalten von Erstellungs-, Lese-, Aktualisierungs- und Löschvorgängen (CRUD) und stellen ihn uns zur Verwendung über die API zur Verfügung, sodass wir nicht wirklich eine Datenbank verwalten müssen. wir dürfen es nur benutzen .
Demo: Erstellen Sie eine Petitionsseite
Wenn wir eine kleine App erstellen möchten, um digitale Unterschriften für eine Petition zu sammeln, müssen wir eine Datenbank einrichten, um diese Unterschriften zu speichern und es der Seite zu ermöglichen, sie zur Anzeige vorzulesen.
Für diese Demo verwenden wir Fauna als unseren DBaaS-Anbieter. Wir werden nicht weiter auf die Funktionsweise von Fauna eingehen, aber um den geringen Aufwand zu demonstrieren, der zum Einrichten einer Datenbank erforderlich ist, listen wir jeden Schritt auf und klicken, um eine einsatzbereite Datenbank zu erhalten:
- Erstellen Sie ein Fauna-Konto unter https://fauna.com
- Klicken Sie auf „Neue Datenbank erstellen“
- Geben Sie der Datenbank einen Namen (z. B. „dynamic-jamstack-demos“)
- Klicken Sie auf „Erstellen“
- Klicken Sie auf der nächsten Seite im linken Menü auf „Sicherheit“.
- Klicken Sie auf „Neuer Schlüssel“
- Ändern Sie das Rollen-Dropdown auf „Server“
- Fügen Sie einen Namen für den Schlüssel hinzu (z. B. „Dynamic JAMstack Demos“)
- Bewahren Sie den Schlüssel für die Verwendung mit der App an einem sicheren Ort auf
- Klicken Sie auf „Speichern“
- Klicken Sie im linken Menü auf „GraphQL“.
- Klicken Sie auf „Schema importieren“
- Laden Sie eine Datei namens
db-schema.gql
, die den folgenden Code enthält:
type Signature { name: String! } type Query { signatures: [Signature!]! }
Sobald wir das Schema hochgeladen haben, ist unsere Datenbank einsatzbereit. (Ernsthaft.)
Dreizehn Schritte sind viel, aber mit diesen dreizehn Schritten haben wir gerade eine Datenbank, eine GraphQL-API, automatische Kapazitätsverwaltung, Skalierung, Bereitstellung, Sicherheit und mehr – alles von Datenbankexperten gehandhabt. Kostenlos. Was für eine Zeit, um am Leben zu sein!
Um es auszuprobieren, bietet uns die Option „GraphQL“ im linken Menü einen GraphQL-Explorer mit Dokumentation zu den verfügbaren Abfragen und Mutationen, mit denen wir CRUD-Operationen durchführen können.
Hinweis : Wir werden in diesem Beitrag nicht auf Details zu GraphQL-Abfragen und -Mutationen eingehen, aber Eve Porcello hat eine hervorragende Einführung zum Senden von GraphQL-Abfragen und -Mutationen geschrieben, wenn Sie eine Einführung in die Funktionsweise wünschen.
Wenn die Datenbank einsatzbereit ist, können wir eine serverlose Funktion erstellen, die neue Signaturen in der Datenbank speichert:
const qs = require('querystring'); const graphql = require('./util/graphql'); exports.handler = async event => { try { // get the signature from the POST data const { signature } = qs.parse(event.body); const ADD_SIGNATURE = ` mutation($signature: String!) { createSignature(data: { name: $signature }) { _id } } `; // store the signature in the database await graphql(ADD_SIGNATURE, { signature }); // send people back to the petition page return { statusCode: 302, headers: { Location: '/03-store-data/', }, // body is unused in 3xx codes, but required in all function responses body: 'redirecting...', }; } catch (error) { return { statusCode: 500, body: JSON.stringify(error.message), }; } };
Diese Funktion bewirkt Folgendes:
- Holt den Signaturwert aus den
POST
-Daten des Formulars - Ruft eine Hilfsfunktion auf, die die Signatur in der Datenbank speichert
- Definiert eine GraphQL-Mutation zum Schreiben in die Datenbank
- Sendet die Mutation mit einer GraphQL-Hilfsfunktion ab
- Leitet zurück zu der Seite, die die Daten übermittelt hat
Als nächstes brauchen wir eine serverlose Funktion, um alle Unterschriften aus der Datenbank auszulesen, damit wir zeigen können, wie viele Menschen unsere Petition unterstützen:
const graphql = require('./util/graphql'); exports.handler = async () => { const { signatures } = await graphql(` query { signatures { data { name } } } `); return { statusCode: 200, body: JSON.stringify(signatures.data), }; };
Diese Funktion sendet eine Anfrage und gibt sie zurück.
Ein wichtiger Hinweis zu sensiblen Schlüsseln und JAMstack-Apps :
Eine Sache, die bei dieser App zu beachten ist, ist, dass wir serverlose Funktionen verwenden, um diese Aufrufe zu tätigen, da wir einen privaten Serverschlüssel an Fauna übergeben müssen, der beweist, dass wir Lese- und Schreibzugriff auf diese Datenbank haben. Wir können diesen Schlüssel nicht in clientseitigen Code einfügen, da dies bedeuten würde, dass jeder ihn im Quellcode finden und ihn verwenden könnte, um CRUD-Operationen für unsere Datenbank durchzuführen. Serverlose Funktionen sind entscheidend, um private Schlüssel in JAMstack-Apps privat zu halten.
Sobald wir unsere serverlosen Funktionen eingerichtet haben, können wir ein Formular hinzufügen, das sich an die Funktion zum Hinzufügen einer Signatur, ein Element zum Anzeigen vorhandener Signaturen und ein wenig JS zum Aufrufen der Funktion zum Abrufen von Signaturen und zum Einfügen in unsere Anzeige sendet Element:
<form action="/.netlify/functions/add-signature" method="POST"> <label for="signature">Your name</label> <input type="text" name="signature" required /> <button type="submit">Sign</button> </form> <ul class="signatures"></ul> <script> fetch('/.netlify/functions/get-signatures') .then(res => res.json()) .then(names => { const signatures = document.querySelector('.signatures'); names.forEach(({ name }) => { const li = document.createElement('li'); li.innerText = name; signatures.appendChild(li); }); }); </script>
Wenn wir dies in den Browser laden, sehen wir unser Petitionsformular mit Unterschriften darunter:
Wenn wir dann unsere Unterschrift hinzufügen …
…und senden Sie es ab, wir sehen unseren Namen am Ende der Liste:
Heißer Digty-Hund! Wir haben gerade eine vollwertige datenbankgestützte JAMstack-App mit etwa 75 Codezeilen und 7 Zeilen Datenbankschema geschrieben!
Schützen Sie Inhalte mit Benutzerauthentifizierung
„Okay, diesmal steckst du sicher fest“, denkst du vielleicht. „Auf keinen Fall kann eine JAMstack-Site die Benutzerauthentifizierung handhaben. Wie zum Teufel würde das überhaupt funktionieren?!“
Ich sage Ihnen, wie es funktioniert, mein Freund: mit unseren bewährten serverlosen Funktionen und OAuth.
OAuth ist ein weit verbreiteter Standard, der es Menschen ermöglicht, Apps eingeschränkten Zugriff auf ihre Kontoinformationen zu gewähren, anstatt ihre Passwörter zu teilen. Wenn Sie sich jemals über einen anderen Dienst bei einem Dienst angemeldet haben (z. B. „mit Ihrem Google-Konto anmelden“), haben Sie OAuth bereits verwendet.
Hinweis: Wir werden nicht weiter auf die Funktionsweise von OAuth eingehen, aber Aaron Parecki hat einen soliden Überblick über OAuth geschrieben, der die Details und den Arbeitsablauf abdeckt.
In JAMstack-Apps können wir OAuth und die JSON Web Tokens (JWTs), die es uns zur Verfügung stellt, nutzen, um Benutzer zu identifizieren, Inhalte zu schützen und nur eingeloggten Benutzern zu erlauben, sie anzuzeigen.
Demo: Anmeldung erforderlich, um geschützte Inhalte anzuzeigen
Wenn wir eine Website erstellen müssen, die Inhalte nur für eingeloggte Benutzer anzeigt, benötigen wir ein paar Dinge:
- Ein Identitätsanbieter, der Benutzer und den Anmeldeablauf verwaltet
- UI-Elemente zum Verwalten des An- und Abmeldens
- Eine serverlose Funktion, die mithilfe von JWTs nach einem angemeldeten Benutzer sucht und geschützte Inhalte zurückgibt, sofern vorhanden
Für dieses Beispiel verwenden wir Netlify Identity, das uns eine wirklich angenehme Entwicklererfahrung zum Hinzufügen von Authentifizierungen bietet und ein Drop-In-Widget zum Verwalten von Anmelde- und Abmeldeaktionen bereitstellt.
Um es zu aktivieren:
- Besuchen Sie Ihr Netlify-Dashboard
- Wählen Sie die Website, für die eine Authentifizierung erforderlich ist, aus Ihrer Websiteliste aus
- Klicken Sie im oberen Navigationsbereich auf „Identität“.
- Klicken Sie auf die Schaltfläche „Identität aktivieren“.
Wir können Netlify Identity zu unserer Website hinzufügen, indem wir Markup hinzufügen, das abgemeldete Inhalte anzeigt, und ein Element hinzufügen, um geschützte Inhalte nach der Anmeldung anzuzeigen:
<div class="content logged-out"> <h1>Super Secret Stuff!</h1> <p> only my bestest friends can see this content</p> <button class="login">log in / sign up to be my best friend</button> </div> <div class="content logged-in"> <div class="secret-stuff"></div> <button class="logout">log out</button> </div>
Dieses Markup basiert auf CSS, um Inhalte basierend darauf anzuzeigen, ob der Benutzer angemeldet ist oder nicht. Allerdings können wir uns darauf nicht verlassen, um den Inhalt tatsächlich zu schützen – jeder könnte den Quellcode einsehen und unsere Geheimnisse stehlen!
Stattdessen haben wir ein leeres div erstellt, das unseren geschützten Inhalt enthält, aber wir müssen eine Anfrage an eine serverlose Funktion stellen, um diesen Inhalt tatsächlich zu erhalten. Wie das funktioniert, werden wir in Kürze untersuchen.
Als Nächstes müssen wir Code hinzufügen, damit unsere Anmeldeschaltfläche funktioniert, den geschützten Inhalt laden und auf dem Bildschirm anzeigen:
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script> <script> const login = document.querySelector('.login'); login.addEventListener('click', () => { netlifyIdentity.open(); }); const logout = document.querySelector('.logout'); logout.addEventListener('click', () => { netlifyIdentity.logout(); }); netlifyIdentity.on('logout', () => { document.querySelector('body').classList.remove('authenticated'); }); netlifyIdentity.on('login', async () => { document.querySelector('body').classList.add('authenticated'); const token = await netlifyIdentity.currentUser().jwt(); const response = await fetch('/.netlify/functions/get-secret-content', { headers: { Authorization: `Bearer ${token}`, }, }).then(res => res.text()); document.querySelector('.secret-stuff').innerHTML = response; }); </script>
Hier ist, was dieser Code tut:
- Lädt das Netlify Identity-Widget, eine Hilfsbibliothek, die ein Anmeldemodal erstellt, den OAuth-Workflow mit Netlify Identity handhabt und unserer App Zugriff auf die Informationen des angemeldeten Benutzers gewährt
- Fügt der Anmeldeschaltfläche einen Ereignis-Listener hinzu, der das Öffnen des Netlify Identity-Anmeldemodus auslöst
- Fügt der Abmeldeschaltfläche einen Ereignis-Listener hinzu, der die Netlify Identity-Abmeldemethode aufruft
- Fügt einen Ereignishandler zum Abmelden hinzu, um die authentifizierte Klasse beim Abmelden zu entfernen, wodurch der angemeldete Inhalt ausgeblendet und der abgemeldete Inhalt angezeigt wird
- Fügt einen Ereignishandler für die Anmeldung hinzu, der:
- Fügt die authentifizierte Klasse hinzu, um den angemeldeten Inhalt anzuzeigen und den abgemeldeten Inhalt auszublenden
- Ruft das JWT des angemeldeten Benutzers ab
- Ruft eine serverlose Funktion zum Laden geschützter Inhalte auf und sendet das JWT im Authorization-Header
- Fügt den geheimen Inhalt in das Secret-Stuff-Div ein, damit angemeldete Benutzer ihn sehen können
Im Moment existiert die serverlose Funktion, die wir in diesem Code aufrufen, nicht. Lassen Sie uns es mit dem folgenden Code erstellen:
exports.handler = async (_event, context) => { try { const { user } = context.clientContext; if (!user) throw new Error('Not Authorized'); return { statusCode: 200, headers: { 'Content-Type': 'text/html', }, body: `
Sie sind eingeladen, ${user.user_metadata.full_name}!
Wenn du das lesen kannst, bedeutet das, dass wir beste Freunde sind.
Hier sind die geheimen Details für meine Geburtstagsparty:
`, }; } Fang (Fehler) { Rückkehr { Statuscode: 401, body: 'Nicht autorisiert', }; } };
jason.af/party
Diese Funktion bewirkt Folgendes:
- Sucht nach einem Benutzer im Kontextargument der serverlosen Funktion
- Löst einen Fehler aus, wenn kein Benutzer gefunden wird
- Gibt geheimen Inhalt zurück, nachdem sichergestellt wurde, dass ein angemeldeter Benutzer ihn angefordert hat
Netlify Functions erkennt Netlify-Identitäts-JWTs in Autorisierungs-Headern und setzt diese Informationen automatisch in einen Kontext – das bedeutet, dass wir nach gültigen JWTs suchen können, ohne Code schreiben zu müssen, um JWTs zu validieren!
Wenn wir diese Seite in unserem Browser laden, sehen wir zuerst die abgemeldete Seite:
Wenn wir auf die Schaltfläche zum Anmelden klicken, sehen wir das Netlify-Identitäts-Widget:
Nach dem Einloggen (oder Registrieren) können wir den geschützten Inhalt sehen:
Wow! Wir haben gerade eine Benutzeranmeldung und geschützte Inhalte zu einer JAMstack-App hinzugefügt!
Was macht man als nächstes
Der JAMstack ist viel mehr als „nur statische Sites“ – wir können auf Benutzerinteraktionen reagieren, Daten speichern, die Benutzerauthentifizierung handhaben und so ziemlich alles andere, was wir auf einer modernen Website tun möchten. Und das alles, ohne dass ein Server bereitgestellt, konfiguriert oder bereitgestellt werden muss!
Was möchten Sie mit dem JAMstack erstellen? Gibt es etwas, von dem Sie immer noch nicht überzeugt sind, dass der JAMstack damit umgehen kann? Ich würde gerne davon hören – melde dich auf Twitter oder in den Kommentaren!