Aggiunta di funzionalità dinamiche e asincrone ai siti JAMstack

Pubblicato: 2022-03-10
Riepilogo rapido ↬ Saltare i server e utilizzare JAMstack per creare e fornire siti Web e app può far risparmiare tempo, denaro e mal di testa consentendoci di fornire solo risorse statiche su una CDN. Ma il compromesso di abbandonare le tradizionali implementazioni basate su server significa che gli approcci standard alle interazioni dinamiche e asincrone nei nostri siti e nelle nostre app non sono più disponibili.

Ciò significa che i siti JAMstack non possono gestire interazioni dinamiche? Sicuramente no!

I siti JAMstack sono ottimi per creare interazioni altamente dinamiche e asincrone. Con alcune piccole modifiche al modo in cui pensiamo al nostro codice, possiamo creare interazioni divertenti e coinvolgenti utilizzando solo risorse statiche!

È sempre più comune vedere siti Web creati utilizzando JAMstack, ovvero siti Web che possono essere serviti come file HTML statici creati da JavaScript, markup e API. Le aziende adorano JAMstack perché riduce i costi dell'infrastruttura, accelera la consegna e abbassa le barriere per prestazioni e miglioramenti della sicurezza perché la spedizione di risorse statiche elimina la necessità di ridimensionare i server o mantenere i database altamente disponibili (il che significa anche che non ci sono server o database che possono essere hackerato). Agli sviluppatori piace JAMstack perché riduce la complessità di far funzionare un sito Web su Internet: non ci sono server da gestire o distribuire; possiamo scrivere codice front-end e va in diretta , come per magia .

("Magic" in questo caso sono implementazioni statiche automatizzate, disponibili gratuitamente da diverse aziende, inclusa Netlify, dove lavoro.)

Ma se passi molto tempo a parlare con gli sviluppatori del JAMstack, sorgerà la domanda se JAMstack sia in grado di gestire o meno applicazioni Web serie. Dopotutto, i siti JAMstack sono siti statici, giusto? E i siti statici non sono super limitati in ciò che possono fare?

Questo è un malinteso molto comune e in questo articolo analizzeremo da dove viene l'idea sbagliata, esamineremo le capacità di JAMstack e analizzeremo diversi esempi di utilizzo di JAMstack per creare applicazioni Web serie.

Fondamenti di JAMstack

Phil Hawksworth spiega cosa significa effettivamente JAMStack e quando ha senso utilizzarlo nei tuoi progetti, nonché come influisce sugli strumenti e sull'architettura front-end. Leggi un articolo correlato →

Altro dopo il salto! Continua a leggere sotto ↓

Cosa rende "statico" un sito JAMstack?

I browser Web di oggi caricano file HTML, CSS e JavaScript, proprio come facevano negli anni '90.

Un sito JAMstack, al suo interno, è una cartella piena di file HTML, CSS e JavaScript.

Si tratta di "asset statici", il che significa che non è necessario un passaggio intermedio per generarli (ad esempio, progetti PHP come WordPress necessitano di un server per generare l'HTML su ogni richiesta).

Questa è la vera potenza di JAMstack: non richiede alcuna infrastruttura specializzata per funzionare. Puoi eseguire un sito JAMstack sul tuo computer locale, inserendolo nella tua rete di distribuzione dei contenuti (CDN) preferita, ospitandolo con servizi come GitHub Pages: puoi persino trascinare la cartella nel tuo client FTP preferito per caricarla all'hosting condiviso.

Gli asset statici non significano necessariamente esperienze statiche

Poiché i siti JAMstack sono costituiti da file statici, è facile presumere che l'esperienza su quei siti sia, sai, static . Ma non è così!

JavaScript è in grado di fare un sacco di cose dinamiche. Dopotutto, i moderni framework JavaScript sono file statici dopo aver completato la fase di creazione e ci sono centinaia di esempi di esperienze di siti Web incredibilmente dinamiche alimentate da loro.

C'è un malinteso comune che "statico" significhi inflessibile o fisso. Ma tutto ciò che "statico" significa in realtà nel contesto dei "siti statici" è che i browser non hanno bisogno di aiuto per fornire i loro contenuti: sono in grado di usarli in modo nativo senza che un server gestisca prima una fase di elaborazione.

Oppure, messo in un altro modo:

"Risorse statiche" non significa app statiche; significa nessun server richiesto.

Il JAMstack può farlo?

Se qualcuno chiede di creare una nuova app, è comune vedere suggerimenti per approcci JAMstack come Gatsby, Eleventy, Nuxt e altri strumenti simili. È altrettanto comune vedere sorgere obiezioni: "i generatori di siti statici non possono fare _______", dove _______ è qualcosa di dinamico.

Ma, come abbiamo accennato nella sezione precedente, i siti JAMstack possono gestire contenuti e interazioni dinamici!

Ecco un elenco incompleto di cose che ho sentito più volte persone affermare che JAMstack non è in grado di gestire che sicuramente può:

  • Carica i dati in modo asincrono
  • Gestire i file di elaborazione, come la manipolazione delle immagini
  • Leggere e scrivere in un database
  • Gestisci l'autenticazione dell'utente e proteggi il contenuto dietro un accesso

Nelle sezioni seguenti, vedremo come implementare ciascuno di questi flussi di lavoro su un sito JAMstack.

Se non vedi l'ora di vedere il JAMstack dinamico in azione, puoi prima dare un'occhiata alle demo, quindi tornare e scoprire come funzionano.

Una nota sulle demo :

Queste demo sono scritte senza alcun framework. Sono solo HTML, CSS e JavaScript standard. Sono stati creati pensando ai browser moderni (ad es. Chrome, Firefox, Safari, Edge) e sfruttano le funzionalità più recenti come i moduli JavaScript, i modelli HTML e l'API Fetch. Non sono stati aggiunti polyfill, quindi se stai utilizzando un browser non supportato, le demo probabilmente falliranno.

Carica i dati da un'API di terze parti in modo asincrono

"E se avessi bisogno di ottenere nuovi dati dopo che i miei file statici sono stati creati?"

In JAMstack, possiamo sfruttare numerose librerie di richieste asincrone, inclusa l'API Fetch integrata, per caricare i dati utilizzando JavaScript in qualsiasi momento.

Demo: cerca un'API di terze parti da un sito JAMstack

Uno scenario comune che richiede il caricamento asincrono è quando il contenuto di cui abbiamo bisogno dipende dall'input dell'utente. Ad esempio, se creiamo una pagina di ricerca per l' API Rick & Morty , non sappiamo quale contenuto visualizzare finché qualcuno non ha inserito un termine di ricerca.

Per gestirlo, dobbiamo:

  1. Crea un modulo in cui le persone possono digitare il termine di ricerca,
  2. Ascolta per l'invio di un modulo,
  3. Ottieni il termine di ricerca dall'invio del modulo,
  4. Invia una richiesta asincrona all'API Rick & Morty utilizzando il termine di ricerca,
  5. Visualizza i risultati della richiesta nella pagina.

Innanzitutto, dobbiamo creare un modulo e un elemento vuoto che conterrà i nostri risultati di ricerca, che assomiglia a questo:

 <form> <label for="name">Find characters by name</label> <input type="text" name="name" required /> <button type="submit">Search</button> </form> <ul></ul>

Successivamente, dobbiamo scrivere una funzione che gestisca gli invii dei moduli. Questa funzione:

  • Impedisci il comportamento di invio del modulo predefinito
  • Ottieni il termine di ricerca dall'input del modulo
  • Utilizza l'API Fetch per inviare una richiesta all'API Rick & Morty utilizzando il termine di ricerca
  • Chiama una funzione di supporto che visualizzi i risultati della ricerca sulla pagina

Abbiamo anche bisogno di aggiungere un listener di eventi nel modulo per l'evento di invio che chiama la nostra funzione di gestione.

Ecco come appare complessivamente quel codice:

 <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>

Nota: per rimanere concentrati sui comportamenti JAMstack dinamici, non discuteremo di come vengono scritte funzioni di utilità come showResults. Tuttavia, il codice è accuratamente commentato, quindi controlla la fonte per sapere come funziona!

Con questo codice in atto, possiamo caricare il nostro sito in un browser e vedremo il modulo vuoto senza risultati che mostrano:

Modulo di ricerca vuoto
Il modulo di ricerca vuoto (Anteprima grande)

Se inseriamo il nome di un personaggio (es. "rick") e clicchiamo "cerca", viene visualizzato un elenco di caratteri i cui nomi contengono "rick":

Modulo di ricerca riempito con "rick" con i caratteri chiamati "Rick" visualizzati di seguito.
Vediamo i risultati della ricerca dopo che il modulo è stato compilato. (Grande anteprima)

Ehi! Quel sito statico ha appena caricato dinamicamente i dati? Santo secchio!

Puoi provarlo tu stesso nella demo dal vivo o controllare il codice sorgente completo per maggiori dettagli.

Gestisci costose attività di elaborazione dal dispositivo dell'utente

In molte app, abbiamo bisogno di fare cose che richiedono molte risorse, come l'elaborazione di un'immagine. Sebbene alcuni di questi tipi di operazioni siano possibili utilizzando solo JavaScript lato client, non è necessariamente una buona idea far funzionare tutto ciò sui dispositivi degli utenti. Se utilizzano un dispositivo a bassa potenza o cercano di prolungare l'ultimo 5% della durata della batteria, far lavorare il proprio dispositivo sarà probabilmente un'esperienza frustrante per loro.

Quindi significa che le app JAMstack sono sfortunate? Affatto!

La "A" in JAMstack sta per API. Ciò significa che possiamo inviare quel lavoro a un'API ed evitare di far girare le ventole dei computer dei nostri utenti fino all'impostazione "hover".

"Ma aspetta", potresti dire. "Se la nostra app deve eseguire un lavoro personalizzato e quel lavoro richiede un'API, non significa solo che stiamo costruendo un server?"

Grazie alla potenza delle funzioni serverless, non è necessario!

Le funzioni serverless (chiamate anche "funzioni lambda") sono una sorta di API senza bisogno di alcun server boilerplate. Possiamo scrivere una semplice vecchia funzione JavaScript e tutto il lavoro di distribuzione, ridimensionamento, routing e così via viene scaricato sul nostro provider serverless preferito.

L'uso di funzioni serverless non significa che non ci sia un server; significa solo che non abbiamo bisogno di pensare a un server.

Le funzioni serverless sono il burro di arachidi per il nostro JAMstack: sbloccano un intero mondo di funzionalità dinamiche ad alta potenza senza mai chiederci di gestire il codice del server o devops.

Demo: converti un'immagine in scala di grigi

Supponiamo di avere un'app che deve:

  • Scarica un'immagine da un URL
  • Converti quell'immagine in scala di grigi
  • Carica l'immagine convertita in un repository GitHub

Per quanto ne so, non c'è modo di eseguire conversioni di immagini del genere interamente nel browser e, anche se ci fosse, è una cosa che richiede molte risorse, quindi probabilmente non vogliamo mettere quel carico sui nostri utenti ' dispositivi.

Invece, possiamo inviare l'URL da convertire in una funzione serverless, che farà il lavoro pesante per noi e invierà un URL a un'immagine convertita.

Per la nostra funzione serverless, utilizzeremo Netlify Functions. Nel codice del nostro sito, aggiungiamo una cartella a livello di root chiamata “functions” e creiamo al suo interno un nuovo file chiamato “convert-image.js”. Quindi scriviamo quello che viene chiamato handler, che è ciò che riceve e, come avrai intuito, gestisce le richieste alla nostra funzione serverless.

Per convertire un'immagine, si presenta così:

 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), }; } };

Questa funzione esegue le seguenti operazioni:

  1. Verifica che la richiesta sia stata inviata utilizzando il metodo HTTP POST
  2. Afferra l'URL dell'immagine dal corpo del POST
  3. Crea una directory temporanea per la memorizzazione dei file che verranno ripuliti al termine dell'esecuzione della funzione
  4. Richiama una funzione di supporto che converte l'immagine in scala di grigi
  5. Chiama una funzione di supporto che carica l'immagine convertita su GitHub
  6. Restituisce un oggetto risposta con un codice di stato HTTP 200 e l'URL dell'immagine appena caricata

Nota : non esamineremo come funziona l'helper per la conversione di immagini o il caricamento su GitHub, ma il codice sorgente è ben commentato in modo da poter vedere come funziona.

Successivamente, dobbiamo aggiungere un modulo che verrà utilizzato per inviare gli URL per l'elaborazione e un luogo in cui mostrare il prima e il dopo:

 <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>

Infine, dobbiamo aggiungere un listener di eventi al modulo in modo da poter inviare gli URL alla nostra funzione serverless per l'elaborazione:

 <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>

Dopo aver distribuito il sito (insieme alla sua nuova cartella "funzioni") su Netlify e/o aver avviato Netlify Dev nella nostra CLI, possiamo vedere il modulo nel nostro browser:

Modulo di conversione immagine vuoto
Un modulo vuoto che accetta un URL immagine (anteprima grande)

Se aggiungiamo l'URL di un'immagine al modulo e facciamo clic su "converti", vedremo "elaborazione in corso..." per un momento mentre la conversione è in corso, quindi vedremo l'immagine originale e la sua controparte in scala di grigi appena creata:

Modulo riempito con un URL immagine, che mostra l'immagine originale in basso a sinistra e l'immagine convertita a destra
L'immagine viene convertita da tutti i colori a scala di grigi. (Grande anteprima)

Oh accidenti! Il nostro sito JAMstack si è appena occupato di affari piuttosto seri e non abbiamo dovuto pensare ai server una volta o scaricare le batterie dei nostri utenti!

Usa un database per archiviare e recuperare le voci

In molte app, avremo inevitabilmente bisogno della possibilità di salvare l'input dell'utente. E questo significa che abbiamo bisogno di un database.

Potresti pensare: "Allora è così, giusto? Il gioco è finito? Sicuramente un sito JAMstack, che ci hai detto è solo una raccolta di file in una cartella, non può essere collegato a un database!

Al contrario.

Come abbiamo visto nella sezione precedente, le funzioni serverless ci danno la possibilità di fare ogni sorta di cose potenti senza dover creare i nostri server.

Allo stesso modo, possiamo utilizzare strumenti database-as-a-service (DBaaS) (come Fauna) per leggere e scrivere su un database senza doverne configurare uno o ospitarlo noi stessi.

Gli strumenti DBaaS semplificano enormemente il processo di creazione di database per i siti Web: creare un nuovo database è semplice quanto definire i tipi di dati che vogliamo archiviare. Gli strumenti generano automaticamente tutto il codice per gestire le operazioni di creazione, lettura, aggiornamento ed eliminazione (CRUD) e lo rendono disponibile per l'uso tramite API, quindi non dobbiamo gestire effettivamente un database; dobbiamo solo usarlo .

Demo: crea una pagina di petizione

Se vogliamo creare una piccola app per raccogliere le firme digitali per una petizione, dobbiamo creare un database per archiviare tali firme e consentire alla pagina di leggerle per la visualizzazione.

Per questa demo useremo Fauna come nostro provider DBaaS. Non approfondiremo come funziona Fauna, ma nell'interesse di dimostrare il piccolo sforzo richiesto per impostare un database, elenchiamo ogni passaggio e facciamo clic per ottenere un database pronto per l'uso:

  1. Crea un account Fauna su https://fauna.com
  2. Fai clic su "crea un nuovo database"
  3. Assegna un nome al database (ad es. "dynamic-jamstack-demos")
  4. Fai clic su "crea"
  5. Fai clic su "sicurezza" nel menu a sinistra nella pagina successiva
  6. Fare clic su "nuova chiave"
  7. Cambia il menu a discesa del ruolo in "Server"
  8. Aggiungi un nome per la chiave (ad es. "Dynamic JAMstack Demos")
  9. Conserva la chiave in un luogo sicuro per l'utilizzo con l'app
  10. Fare clic su "salva"
  11. Fai clic su "GraphQL" nel menu a sinistra
  12. Fai clic su "importa schema"
  13. Carica un file chiamato db-schema.gql che contiene il seguente codice:
 type Signature { name: String! } type Query { signatures: [Signature!]! }

Una volta caricato lo schema, il nostro database è pronto per l'uso. (Sul serio.)

Tredici passaggi sono molti, ma con quei tredici passaggi abbiamo appena ottenuto un database, un'API GraphQL, la gestione automatica della capacità, il ridimensionamento, l'implementazione, la sicurezza e altro ancora, il tutto gestito da esperti di database. Gratuito. Che tempo per essere vivi!

Per provarlo, l'opzione "GraphQL" nel menu di sinistra ci fornisce un esploratore GraphQL con documentazione sulle query e mutazioni disponibili che ci consentono di eseguire operazioni CRUD.

Nota : in questo post non entreremo nei dettagli delle query e delle mutazioni di GraphQL, ma Eve Porcello ha scritto un'eccellente introduzione all'invio di query e mutazioni di GraphQL se desideri un'introduzione su come funziona.

Con il database pronto per l'uso, possiamo creare una funzione serverless che memorizza le nuove firme nel database:

 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), }; } };

Questa funzione esegue le seguenti operazioni:

  1. Afferra il valore della firma dai dati POST del modulo
  2. Richiama una funzione di supporto che archivia la firma nel database
  3. Definisce una mutazione GraphQL da scrivere nel database
  4. Invia la mutazione utilizzando una funzione di supporto GraphQL
  5. Reindirizzamento alla pagina che ha inviato i dati

Successivamente, abbiamo bisogno di una funzione serverless per leggere tutte le firme dal database in modo da poter mostrare quante persone supportano la nostra petizione:

 const graphql = require('./util/graphql'); exports.handler = async () => { const { signatures } = await graphql(` query { signatures { data { name } } } `); return { statusCode: 200, body: JSON.stringify(signatures.data), }; };

Questa funzione invia una query e la restituisce.

Una nota importante sulle chiavi sensibili e sulle app JAMstack :

Una cosa da notare su questa app è che stiamo usando funzioni serverless per effettuare queste chiamate perché dobbiamo passare una chiave del server privato a Fauna che dimostri che abbiamo accesso in lettura e scrittura a questo database. Non possiamo inserire questa chiave nel codice lato client, perché ciò significherebbe che chiunque potrebbe trovarla nel codice sorgente e utilizzarla per eseguire operazioni CRUD sul nostro database. Le funzioni serverless sono fondamentali per mantenere private le chiavi private nelle app JAMstack.

Una volta impostate le nostre funzioni serverless, possiamo aggiungere un modulo che si sottopone alla funzione per aggiungere una firma, un elemento per mostrare le firme esistenti e un po' di JS per chiamare la funzione per ottenere le firme e inserirle nel nostro display elemento:

 <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>

Se lo carichiamo nel browser, vedremo il nostro modulo di petizione con le firme sotto di esso:

Modulo di petizione vuoto con un elenco di firme di seguito
Un modulo vuoto che accetta una firma digitale (anteprima grande)

Quindi, se aggiungiamo la nostra firma...

Modulo di petizione con un nome nel campo, ma non ancora inviato
Il modulo di petizione con un nome compilato (anteprima grande)

…e invialo, vedremo il nostro nome aggiunto in fondo all'elenco:

Modulo di petizione vuoto con la nuova firma in fondo all'elenco
Il modulo di richiesta viene cancellato e la nuova firma viene aggiunta in fondo all'elenco. (Grande anteprima)

Caldo cane diggino! Abbiamo appena scritto un'app JAMstack completa basata su database con circa 75 righe di codice e 7 righe di schema del database!

Proteggi il contenuto con l'autenticazione dell'utente

"Okay, questa volta sei sicuramente bloccato", potresti pensare. “Non è possibile che un sito JAMstack possa gestire l'autenticazione degli utenti. Come diavolo funzionerebbe, anche?!”

Ti dirò come funziona, amico mio: con le nostre affidabili funzioni serverless e OAuth.

OAuth è uno standard ampiamente adottato per consentire alle persone di concedere alle app un accesso limitato alle informazioni del proprio account anziché condividere le proprie password. Se hai mai effettuato l'accesso a un servizio utilizzando un altro servizio (ad esempio, "accedi con il tuo account Google"), hai già utilizzato OAuth.

Nota: non approfondiremo il funzionamento di OAuth, ma Aaron Parecki ha scritto una solida panoramica di OAuth che copre i dettagli e il flusso di lavoro.

Nelle app JAMstack, possiamo sfruttare OAuth e i token Web JSON (JWT) che ci fornisce per identificare gli utenti, proteggere i contenuti e consentire la visualizzazione solo agli utenti che hanno effettuato l'accesso.

Demo: richiede l'accesso per visualizzare i contenuti protetti

Se dobbiamo creare un sito che mostri il contenuto solo agli utenti che hanno effettuato l'accesso, abbiamo bisogno di alcune cose:

  1. Un provider di identità che gestisce gli utenti e il flusso di accesso
  2. Elementi dell'interfaccia utente per gestire l'accesso e il logout
  3. Una funzione serverless che verifica la presenza di un utente connesso utilizzando JWT e restituisce contenuto protetto, se fornito

Per questo esempio, useremo Netlify Identity, che ci offre un'esperienza di sviluppo davvero piacevole per l'aggiunta dell'autenticazione e fornisce un widget drop-in per la gestione delle azioni di accesso e disconnessione.

Per abilitarlo:

  • Visita la tua dashboard Netlify
  • Scegli il sito che necessita di autenticazione dall'elenco dei siti
  • Fai clic su "identità" nella barra di navigazione in alto
  • Fare clic sul pulsante "Abilita identità".

Possiamo aggiungere Netlify Identity al nostro sito aggiungendo markup che mostra il contenuto disconnesso e aggiunge un elemento per mostrare il contenuto protetto dopo l'accesso:

 <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>

Questo markup si basa sui CSS per mostrare il contenuto in base al fatto che l'utente abbia effettuato l'accesso o meno. Tuttavia, non possiamo fare affidamento su questo per proteggere effettivamente il contenuto: chiunque potrebbe visualizzare il codice sorgente e rubare i nostri segreti!

Invece, abbiamo creato un div vuoto che conterrà il nostro contenuto protetto, ma dovremo fare una richiesta a una funzione serverless per ottenere effettivamente quel contenuto. Approfondiremo come funziona a breve.

Successivamente, dobbiamo aggiungere il codice per far funzionare il nostro pulsante di accesso, caricare il contenuto protetto e mostrarlo sullo schermo:

 <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>

Ecco cosa fa questo codice:

  1. Carica il widget Netlify Identity, che è una libreria di supporto che crea una modalità di accesso, gestisce il flusso di lavoro OAuth con Netlify Identity e fornisce alla nostra app l'accesso alle informazioni dell'utente connesso
  2. Aggiunge un listener di eventi al pulsante di accesso che attiva la modalità di accesso di Netlify Identity per l'apertura
  3. Aggiunge un listener di eventi al pulsante di disconnessione che chiama il metodo di disconnessione di Netlify Identity
  4. Aggiunge un gestore eventi per il logout per rimuovere la classe autenticata al logout, che nasconde il contenuto connesso e mostra il contenuto disconnesso
  5. Aggiunge un gestore eventi per l'accesso che:
    1. Aggiunge la classe autenticata per mostrare il contenuto connesso e nascondere il contenuto disconnesso
    2. Afferra il JWT dell'utente che ha effettuato l'accesso
    3. Richiama una funzione serverless per caricare contenuto protetto, inviando il JWT nell'intestazione di autorizzazione
    4. Inserisce il contenuto segreto nel div secret-stuff in modo che gli utenti che hanno effettuato l'accesso possano vederlo

In questo momento la funzione serverless che stiamo chiamando in quel codice non esiste. Creiamolo con il seguente codice:

 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: `

Sei invitato, ${user.user_metadata.full_name}!

Se riesci a leggere questo significa che siamo migliori amici.

Ecco i dettagli segreti per la mia festa di compleanno:
jason.af/party

`, }; } cattura (errore) { Restituzione { codice di stato: 401, corpo: 'Non Autorizzato', }; } };

Questa funzione esegue le seguenti operazioni:

  1. Verifica la presenza di un utente nell'argomento di contesto della funzione serverless
  2. Genera un errore se non viene trovato nessun utente
  3. Restituisce contenuto segreto dopo essersi assicurati che un utente connesso lo abbia richiesto

Netlify Functions rileverà i JWT Netlify Identity nelle intestazioni di autorizzazione e inserirà automaticamente tali informazioni nel contesto: ciò significa che possiamo verificare la presenza di JWT validi senza dover scrivere codice per convalidare i JWT!

Quando carichiamo questa pagina nel nostro browser, vedremo prima la pagina disconnessa:

Vista disconnessa che mostra informazioni sull'accesso o sulla creazione di un account
Una volta disconnesso, possiamo vedere solo le informazioni sull'accesso. (Anteprima grande)

Se facciamo clic sul pulsante per accedere, vedremo il widget Netlify Identity:

Una finestra modale che mostra le schede di registrazione e di accesso con un modulo di accesso visualizzato
Netlify Identity Widget fornisce l'intera esperienza di accesso/registrazione. (Grande anteprima)

Dopo aver effettuato il login (o esserti registrati), possiamo vedere il contenuto protetto:

Visualizzazione dell'accesso che mostra le informazioni su una festa di compleanno
Dopo aver effettuato l'accesso, possiamo vedere il contenuto protetto. (Grande anteprima)

Wowe! Abbiamo appena aggiunto l'accesso utente e il contenuto protetto a un'app JAMstack!

Cosa fare dopo

Il JAMstack è molto più che "solo siti statici": possiamo rispondere alle interazioni degli utenti, archiviare dati, gestire l'autenticazione degli utenti e praticamente qualsiasi altra cosa vogliamo fare su un sito Web moderno. E tutto senza la necessità di fornire, configurare o distribuire un server!

Cosa vuoi costruire con JAMstack? C'è qualcosa che non sei ancora convinto che JAMstack possa gestire? Mi piacerebbe sentirne parlare: contattami su Twitter o nei commenti!