Adăugarea de funcționalități dinamice și asincrone la site-urile JAMstack

Publicat: 2022-03-10
Rezumat rapid ↬ Omiterea serverelor și utilizarea JAMstack pentru a construi și livra site-uri web și aplicații poate economisi timp, bani și dureri de cap, permițându-ne să livrăm numai active statice pe un CDN. Dar compromisul renunțării la implementările tradiționale bazate pe server înseamnă că abordările standard ale interacțiunilor dinamice și asincrone din site-urile și aplicațiile noastre nu mai sunt disponibile.

Înseamnă asta că site-urile JAMstack nu pot gestiona interacțiunile dinamice? Cu siguranta nu!

Site-urile JAMstack sunt excelente pentru a crea interacțiuni extrem de dinamice, asincrone. Cu câteva mici ajustări ale modului în care gândim codul nostru, putem crea interacțiuni distractive și captivante folosind doar elemente statice!

Este din ce în ce mai obișnuit să vezi site-uri web create folosind JAMstack - adică site-uri web care pot fi servite ca fișiere HTML statice create din JavaScript, Markup și API-uri. Companiile iubesc JAMstack deoarece reduce costurile de infrastructură, accelerează livrarea și scade barierele pentru îmbunătățirea performanței și a securității, deoarece expedierea activelor statice elimină nevoia de a scala serverele sau de a menține bazele de date foarte disponibile (ceea ce înseamnă, de asemenea, că nu există servere sau baze de date care pot fi piratat). Dezvoltatorilor le place JAMstack pentru că reduce complexitatea realizării unui site web pe internet: nu există servere de administrat sau implementat; putem scrie cod front-end și pur și simplu merge live , ca prin magie .

(„Magia” în acest caz este implementări statice automate, care sunt disponibile gratuit de la o serie de companii, inclusiv Netlify, unde lucrez.)

Dar dacă petreceți mult timp vorbind cu dezvoltatorii despre JAMstack, va apărea întrebarea dacă JAMstack poate gestiona aplicațiile web serioase. La urma urmei, site-urile JAMstack sunt site-uri statice, nu? Și nu sunt site-urile statice foarte limitate în ceea ce pot face?

Aceasta este o concepție greșită cu adevărat obișnuită, iar în acest articol ne vom scufunda de unde vine ideea greșită, ne vom uita la capacitățile JAMstack-ului și vom parcurge câteva exemple de utilizare a JAMstack-ului pentru a construi aplicații web serioase.

Fundamentele JAMstack

Phil Hawksworth explică ce înseamnă de fapt JAMStack și când are sens să-l folosești în proiectele tale, precum și cum afectează instrumentele și arhitectura front-end. Citiți un articol înrudit →

Mai multe după săritură! Continuați să citiți mai jos ↓

Ce face ca un site JAMstack să fie „static”?

Browserele web încarcă astăzi fișiere HTML, CSS și JavaScript, la fel cum făceau în anii '90.

Un site JAMstack, în esență, este un folder plin de fișiere HTML, CSS și JavaScript.

Acestea sunt „active statice”, adică nu avem nevoie de un pas intermediar pentru a le genera (de exemplu, proiectele PHP precum WordPress au nevoie de un server pentru a genera codul HTML la fiecare solicitare).

Aceasta este adevărata putere a JAMstack: nu necesită nicio infrastructură specializată pentru a funcționa. Puteți rula un site JAMstack pe computerul dvs. local, punându-l în rețeaua preferată de livrare a conținutului (CDN), găzduindu-l cu servicii precum GitHub Pages - puteți chiar să trageți și să plasați folderul în clientul dvs. FTP preferat pentru a-l încărca la găzduire partajată.

Activele statice nu înseamnă neapărat experiențe statice

Deoarece site-urile JAMstack sunt făcute din fișiere statice, este ușor să presupunem că experiența pe acele site-uri este, știți, statică . Dar nu este cazul!

JavaScript este capabil să facă o mulțime de lucruri dinamice. La urma urmei, cadrele JavaScript moderne sunt fișiere statice după ce trecem prin pasul de construire - și există sute de exemple de experiențe de site web incredibil de dinamice bazate pe acestea.

Există o concepție greșită comună că „static” înseamnă inflexibil sau fix. Dar tot ceea ce înseamnă „static” cu adevărat în contextul „site-urilor statice” este că browserele nu au nevoie de ajutor pentru a-și livra conținutul – le pot folosi nativ fără ca un server să gestioneze mai întâi un pas de procesare.

Sau, altfel spus:

„Active statice” nu înseamnă aplicații statice; înseamnă că nu este nevoie de server.

Poate JAMstack să facă asta?

Dacă cineva întreabă despre construirea unei noi aplicații, este obișnuit să vedeți sugestii pentru abordări JAMstack, cum ar fi Gatsby, Eleventy, Nuxt și alte instrumente similare. Este la fel de comun să vezi că apar obiecții: „generatorii statici de site nu pot face _______”, unde _______ este ceva dinamic.

Dar – așa cum am atins în secțiunea anterioară – site-urile JAMstack pot gestiona conținut dinamic și interacțiuni!

Iată o listă incompletă de lucruri pe care am auzit în mod repetat că oamenii susțin că JAMstack nu le poate gestiona și cu siguranță le poate face:

  • Încărcați datele în mod asincron
  • Gestionați procesarea fișierelor, cum ar fi manipularea imaginilor
  • Citiți și scrieți într-o bază de date
  • Gestionați autentificarea utilizatorului și protejați conținutul din spatele unei autentificări

În secțiunile următoare, vom analiza cum să implementăm fiecare dintre aceste fluxuri de lucru pe un site JAMstack.

Dacă abia așteptați să vedeți JAMstack-ul dinamic în acțiune, puteți consulta mai întâi demonstrațiile, apoi reveniți și aflați cum funcționează.

O notă despre demonstrații :

Aceste demonstrații sunt scrise fără cadre. Sunt doar HTML, CSS și JavaScript standard. Au fost construite având în vedere browserele moderne (de exemplu, Chrome, Firefox, Safari, Edge) și profită de funcții mai noi precum modulele JavaScript, șabloanele HTML și API-ul Fetch. Nu au fost adăugate polyfill-uri, așa că dacă utilizați un browser neacceptat, demonstrațiile probabil vor eșua.

Încărcați datele dintr-un API terță parte în mod asincron

„Ce se întâmplă dacă trebuie să obțin date noi după ce fișierele mele statice sunt create?”

În JAMstack, putem profita de numeroase biblioteci de solicitări asincrone, inclusiv API-ul Fetch încorporat, pentru a încărca date folosind JavaScript în orice moment.

Demo: Căutați într-un API terță parte de pe un site JAMstack

Un scenariu comun care necesită încărcare asincronă este atunci când conținutul de care avem nevoie depinde de intrarea utilizatorului. De exemplu, dacă construim o pagină de căutare pentru API-ul Rick & Morty , nu știm ce conținut să afișam până când cineva a introdus un termen de căutare.

Pentru a gestiona asta, trebuie să:

  1. Creați un formular în care oamenii pot introduce termenul de căutare,
  2. Ascultă trimiterea unui formular,
  3. Obțineți termenul de căutare din trimiterea formularului,
  4. Trimiteți o solicitare asincronă către API-ul Rick & Morty folosind termenul de căutare,
  5. Afișați rezultatele solicitării pe pagină.

În primul rând, trebuie să creăm un formular și un element gol care va conține rezultatele căutării noastre, care arată astfel:

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

Apoi, trebuie să scriem o funcție care se ocupă de trimiterile formularelor. Această funcție va:

  • Preveniți comportamentul implicit de trimitere a formularelor
  • Obțineți termenul de căutare din introducerea formularului
  • Utilizați API-ul Fetch pentru a trimite o solicitare către API-ul Rick & Morty folosind termenul de căutare
  • Apelați o funcție de ajutor care afișează rezultatele căutării pe pagină

De asemenea, trebuie să adăugăm un ascultător de evenimente în formularul pentru evenimentul de trimitere care apelează funcția noastră de gestionare.

Iată cum arată codul respectiv:

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

Notă: pentru a rămâne concentrat pe comportamentele dinamice JAMstack, nu vom discuta despre modul în care sunt scrise funcțiile de utilitate precum showResults. Totuși, codul este bine comentat, așa că verificați sursa pentru a afla cum funcționează!

Cu acest cod introdus, ne putem încărca site-ul într-un browser și vom vedea formularul gol, fără rezultate:

Formular de căutare gol
Formularul de căutare gol (previzualizare mare)

Dacă introducem un nume de caracter (ex. „rick”) și facem clic pe „căutare”, vedem afișată o listă de personaje ale căror nume conțin „rick”:

Formular de căutare completat cu „rick” cu caractere numite „Rick” afișate mai jos.
Vedem rezultatele căutării după ce formularul este completat. (Previzualizare mare)

Hei! Site-ul static a încărcat date dinamic? Sfinte găleți!

Puteți încerca acest lucru pentru dvs. în demonstrația live sau puteți verifica întregul cod sursă pentru mai multe detalii.

Gestionați sarcinile de calcul costisitoare de pe dispozitivul utilizatorului

În multe aplicații, trebuie să facem lucruri care necesită destul de mult resurse, cum ar fi procesarea unei imagini. Deși unele dintre aceste tipuri de operațiuni sunt posibile numai folosind JavaScript la nivel de client, nu este neapărat o idee grozavă să faceți ca dispozitivele utilizatorilor dvs. să facă toată această funcționare. Dacă se află pe un dispozitiv cu putere redusă sau încearcă să-și prelungească ultimii 5% din durata de viață a bateriei, a-și face dispozitivul să lucreze mult va fi probabil o experiență frustrantă pentru ei.

Deci asta înseamnă că aplicațiile JAMstack nu au noroc? Deloc!

„A” din JAMstack înseamnă API-uri. Aceasta înseamnă că putem trimite acea lucrare către un API și să evităm să transformăm fanii computerului utilizatorilor noștri la setarea „hover”.

„Dar stai”, ai putea spune. „Dacă aplicația noastră trebuie să efectueze lucrări personalizate și această activitate necesită un API, asta nu înseamnă doar că construim un server?”

Datorită puterii funcțiilor fără server, nu trebuie să facem acest lucru!

Funcțiile fără server (numite și „funcții lambda”) sunt un fel de API fără a fi necesară nicio placă standard de server. Ajungem să scriem o funcție JavaScript veche, iar toată munca de implementare, scalare, rutare și așa mai departe este descărcată către furnizorul nostru fără server ales.

Utilizarea funcțiilor fără server nu înseamnă că nu există un server; înseamnă doar că nu trebuie să ne gândim la un server.

Funcțiile fără server sunt untul de arahide pentru JAMstack-ul nostru: ele deblochează o lume întreagă de funcționalități dinamice de mare putere, fără a ne cere vreodată să ne ocupăm de codul serverului sau devops.

Demo: convertiți o imagine în tonuri de gri

Să presupunem că avem o aplicație care trebuie să:

  • Descărcați o imagine de la o adresă URL
  • Convertiți acea imagine în tonuri de gri
  • Încărcați imaginea convertită într-un depozit GitHub

Din câte știu eu, nu există nicio modalitate de a face conversii de imagini ca acestea în întregime în browser - și chiar dacă ar exista, este un lucru care necesită destul de mult resurse, așa că probabil că nu vrem să punem această sarcină asupra utilizatorilor noștri. ' dispozitive.

În schimb, putem trimite adresa URL pentru a fi convertită într-o funcție fără server, care va face munca grea pentru noi și va trimite înapoi o adresă URL la o imagine convertită.

Pentru funcția noastră fără server, vom folosi funcțiile Netlify. În codul site-ului nostru, adăugăm un folder la nivel rădăcină numit „funcții” și creăm un nou fișier numit „convert-image.js” în interior. Apoi scriem ceea ce se numește un handler, care este ceea ce primește și - după cum probabil ați ghicit - gestionează cererile către funcția noastră fără server.

Pentru a converti o imagine, arată astfel:

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

Această funcție face următoarele:

  1. Verifică pentru a se asigura că solicitarea a fost trimisă folosind metoda HTTP POST
  2. Preia adresa URL a imaginii din corpul POST
  3. Creează un director temporar pentru stocarea fișierelor care vor fi curățate odată ce funcția este executată
  4. Apelează o funcție de ajutor care convertește imaginea în tonuri de gri
  5. Apelează o funcție de ajutor care încarcă imaginea convertită în GitHub
  6. Returnează un obiect de răspuns cu un cod de stare HTTP 200 și adresa URL a imaginii nou încărcate

Notă : Nu vom analiza modul în care funcționează ajutorul pentru conversia imaginilor sau pentru încărcarea în GitHub, dar codul sursă este bine comentat, astfel încât să puteți vedea cum funcționează.

Apoi, trebuie să adăugăm un formular care va fi folosit pentru a trimite adrese URL pentru procesare și un loc pentru a afișa înainte și după:

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

În cele din urmă, trebuie să adăugăm un ascultător de evenimente în formular, astfel încât să putem trimite adresele URL către funcția noastră fără server pentru procesare:

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

După implementarea site-ului (împreună cu noul său folder „funcții”) în Netlify și/sau pornirea Netlify Dev în CLI-ul nostru, putem vedea formularul în browser:

Formular gol de conversie a imaginii
Un formular gol care acceptă o adresă URL a imaginii (previzualizare mare)

Dacă adăugăm o adresă URL a imaginii în formular și facem clic pe „conversie”, vom vedea „procesare...” pentru un moment în timp ce conversia are loc, apoi vom vedea imaginea originală și omologul său în tonuri de gri nou creat:

Formular completat cu o adresă URL a imaginii, care arată imaginea originală de mai jos în stânga și imaginea convertită în dreapta
Imaginea este convertită de la culoare în tonuri de gri. (Previzualizare mare)

Oh la naiba! Site-ul nostru JAMstack tocmai s-a ocupat de niște afaceri destul de serioase și nu a trebuit să ne gândim o dată la servere sau să consumăm bateriile utilizatorilor noștri!

Utilizați o bază de date pentru a stoca și a prelua intrări

În multe aplicații, vom avea inevitabil nevoie de capacitatea de a salva intrarea utilizatorului. Și asta înseamnă că avem nevoie de o bază de date.

S-ar putea să vă gândiți: „Deci asta este, nu? Nu mai ține șmecheria? Cu siguranță un site JAMstack – despre care ne-ați spus că este doar o colecție de fișiere dintr-un folder – nu poate fi conectat la o bază de date!”

Dimpotrivă.

După cum am văzut în secțiunea anterioară, funcțiile fără server ne oferă posibilitatea de a face tot felul de lucruri puternice fără a fi nevoie să ne creăm propriile servere.

În mod similar, putem folosi instrumente de bază de date ca serviciu (DBaaS) (cum ar fi Fauna) pentru a citi și scrie într-o bază de date fără a fi nevoie să setăm una sau să o găzduim noi înșine.

Instrumentele DBaaS simplifică masiv procesul de configurare a bazelor de date pentru site-uri web: crearea unei noi baze de date este la fel de simplă ca și definirea tipurilor de date pe care dorim să le stocăm. Instrumentele generează automat tot codul pentru a gestiona operațiunile de creare, citire, actualizare și ștergere (CRUD) și pentru a-l pune la dispoziție pentru utilizare prin API, astfel încât să nu fie nevoiți să gestionăm efectiv o bază de date; doar ajungem să-l folosim .

Demo: Creați o pagină de petiție

Dacă dorim să creăm o aplicație mică pentru a colecta semnături digitale pentru o petiție, trebuie să creăm o bază de date pentru a stoca acele semnături și a permite paginii să le citească pentru afișare.

Pentru această demonstrație vom folosi Fauna ca furnizor DBaaS. Nu vom aprofunda modul în care funcționează Fauna, dar în interesul de a demonstra efortul mic necesar pentru a configura o bază de date, să enumerăm fiecare pas și să facem clic pentru a obține o bază de date gata de utilizare:

  1. Creați un cont Fauna la https://fauna.com
  2. Faceți clic pe „creați o nouă bază de date”
  3. Dați un nume bazei de date (de exemplu, „dynamic-jamstack-demos”)
  4. Faceți clic pe „creați”
  5. Faceți clic pe „securitate” în meniul din stânga de pe pagina următoare
  6. Faceți clic pe „cheie nouă”
  7. Schimbați meniul drop-down pentru rol în „Server”
  8. Adăugați un nume pentru cheie (de exemplu, „Dynamic JAMstack Demos”)
  9. Păstrați cheia într-un loc sigur pentru a fi utilizată cu aplicația
  10. Faceți clic pe „salvare”
  11. Faceți clic pe „GraphQL” în meniul din stânga
  12. Faceți clic pe „import schema”
  13. Încărcați un fișier numit db-schema.gql care conține următorul cod:
 type Signature { name: String! } type Query { signatures: [Signature!]! }

Odată ce încărcăm schema, baza noastră de date este gata de utilizare. (Serios.)

Treisprezece pași sunt mulți, dar cu cei treisprezece pași, tocmai am primit o bază de date, un API GraphQL, gestionarea automată a capacității, scalarea, implementarea, securitatea și multe altele - toate gestionate de experți în baze de date. Gratuit. Ce vremuri trăim!

Pentru a o încerca, opțiunea „GraphQL” din meniul din stânga ne oferă un explorator GraphQL cu documentație privind interogările și mutațiile disponibile care ne permit să efectuăm operațiuni CRUD.

Notă : Nu vom intra în detalii despre interogările și mutațiile GraphQL în această postare, dar Eve Porcello a scris o introducere excelentă pentru trimiterea de interogări și mutații GraphQL dacă doriți un primer despre cum funcționează.

Cu baza de date gata de funcționare, putem crea o funcție fără server care stochează noi semnături în baza de date:

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

Această funcție face următoarele:

  1. Preia valoarea semnăturii din datele formularului POST
  2. Apelează o funcție de ajutor care stochează semnătura în baza de date
  3. Definește o mutație GraphQL pentru a scrie în baza de date
  4. Trimite mutația folosind o funcție de ajutor GraphQL
  5. Redirecționează înapoi la pagina care a trimis datele

În continuare, avem nevoie de o funcție fără server pentru a citi toate semnăturile din baza de date, astfel încât să putem arăta câți oameni susțin petiția noastră:

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

Această funcție trimite o interogare și o returnează.

O notă importantă despre cheile sensibile și aplicațiile JAMstack :

Un lucru de remarcat despre această aplicație este că folosim funcții fără server pentru a efectua aceste apeluri, deoarece trebuie să transmitem Fauna o cheie de server privată care să demonstreze că avem acces de citire și scriere la această bază de date. Nu putem pune această cheie în codul clientului, deoarece asta ar însemna că oricine ar putea să o găsească în codul sursă și să o folosească pentru a efectua operațiuni CRUD în baza noastră de date. Funcțiile fără server sunt esențiale pentru a păstra cheile private private în aplicațiile JAMstack.

Odată ce avem funcțiile noastre fără server configurate, putem adăuga un formular care se trimite la funcția pentru adăugarea unei semnături, un element pentru a afișa semnăturile existente și un pic de JS pentru a apela funcția pentru a obține semnături și a le pune în afișajul nostru. 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>

Dacă încărcăm acest lucru în browser, vom vedea formularul nostru de petiție cu semnături sub el:

Formular de petiție gol, cu o listă de semnături mai jos
Un formular gol care acceptă o semnătură digitală (previzualizare mare)

Apoi, dacă adăugăm semnătura noastră...

Formular de petiție cu un nume în câmp, dar nedepus încă
Formularul de petiție cu un nume completat (previzualizare mare)

… și trimiteți-l, vom vedea numele nostru atașat în partea de jos a listei:

Formular de petiție gol cu ​​noua semnătură în partea de jos a listei
Formularul de petiție este șters și noua semnătură este adăugată în partea de jos a listei. (Previzualizare mare)

Câine fierbinte! Tocmai am scris o aplicație JAMstack complet alimentată de baze de date, cu aproximativ 75 de linii de cod și 7 linii de schemă de bază de date!

Protejați conținutul cu autentificarea utilizatorului

„Bine, cu siguranță ești blocat de data asta”, s-ar putea să te gândești. „Nu există nicio modalitate prin care un site JAMstack poate gestiona autentificarea utilizatorilor. Cum naiba ar funcționa asta, chiar?!”

Îți voi spune cum funcționează, prietene: cu funcțiile noastre de încredere fără server și OAuth.

OAuth este un standard adoptat pe scară largă pentru a permite oamenilor să acorde aplicațiilor acces limitat la informațiile contului lor, mai degrabă decât să-și partajeze parolele. Dacă v-ați conectat vreodată la un serviciu folosind un alt serviciu (de exemplu, „conectați-vă cu contul dvs. Google”), ați mai folosit OAuth.

Notă: nu vom aprofunda modul în care funcționează OAuth, dar Aaron Parecki a scris o prezentare generală solidă a OAuth care acoperă detaliile și fluxul de lucru.

În aplicațiile JAMstack, putem profita de OAuth și de JSON Web Tokens (JWT) pe care ni le oferă pentru a identifica utilizatorii, pentru a proteja conținutul și pentru a permite numai utilizatorilor conectați să-l vizualizeze.

Demo: necesită autentificare pentru a vedea conținutul protejat

Dacă trebuie să construim un site care să arate conținut numai pentru utilizatorii conectați, avem nevoie de câteva lucruri:

  1. Un furnizor de identitate care gestionează utilizatorii și fluxul de conectare
  2. Elemente UI pentru a gestiona autentificarea și deconectarea
  3. O funcție fără server care verifică un utilizator conectat folosind JWT și returnează conținut protejat dacă este furnizat unul

Pentru acest exemplu, vom folosi Netlify Identity, care ne oferă o experiență de dezvoltator cu adevărat plăcută pentru adăugarea de autentificare și oferă un widget drop-in pentru gestionarea acțiunilor de conectare și deconectare.

Pentru a-l activa:

  • Vizitați tabloul de bord Netlify
  • Alegeți site-ul care necesită autentificare din lista dvs. de site-uri
  • Faceți clic pe „identitate” în partea de sus a navigației
  • Faceți clic pe butonul „Activați identitatea”.

Putem adăuga Netlify Identity pe site-ul nostru adăugând un marcaj care arată conținutul deconectat și adaugă un element pentru a afișa conținutul protejat după conectare:

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

Acest marcaj se bazează pe CSS pentru a afișa conținutul în funcție de faptul dacă utilizatorul este conectat sau nu. Cu toate acestea, nu ne putem baza pe asta pentru a proteja efectiv conținutul - oricine poate vedea codul sursă și ne poate fura secretele!

În schimb, am creat un div gol care va conține conținutul nostru protejat, dar va trebui să facem o solicitare unei funcții fără server pentru a obține efectiv acel conținut. Vom cerceta cum funcționează în curând.

Apoi, trebuie să adăugăm cod pentru ca butonul de conectare să funcționeze, să încărcăm conținutul protejat și să îl arătăm pe ecran:

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

Iată ce face acest cod:

  1. Încarcă widgetul Netlify Identity, care este o bibliotecă de ajutor care creează un mod de conectare, gestionează fluxul de lucru OAuth cu Netlify Identity și oferă aplicației noastre acces la informațiile utilizatorului conectat.
  2. Adaugă un ascultător de evenimente la butonul de conectare care declanșează deschiderea modului de conectare Netlify Identity
  3. Adaugă un ascultător de evenimente la butonul de deconectare care apelează metoda de deconectare Netlify Identity
  4. Adaugă un handler de evenimente pentru deconectare pentru a elimina clasa autentificată la deconectare, care ascunde conținutul conectat și arată conținutul deconectat
  5. Adaugă un handler de evenimente pentru autentificare care:
    1. Adaugă clasa autentificată pentru a afișa conținutul conectat și pentru a ascunde conținutul deconectat
    2. Preia JWT-ul utilizatorului conectat
    3. Apelează o funcție fără server pentru a încărca conținut protejat, trimițând JWT în antetul Autorizare
    4. Pune conținutul secret în div-ul secret-lucruri, astfel încât utilizatorii conectați să îl poată vedea

Momentan, funcția serverless pe care o apelăm în acel cod nu există. Să-l creăm cu următorul cod:

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

Sunteți invitat, ${user.user_metadata.full_name}!

Dacă poți citi asta înseamnă că suntem cei mai buni prieteni.

Iată detaliile secrete pentru petrecerea mea de naștere:
jason.af/party

`, }; } captură (eroare) { întoarcere { statusCod: 401, body: „Neautorizat”, }; } };

Această funcție face următoarele:

  1. Verifică un utilizator în argumentul context al funcției fără server
  2. Afișează o eroare dacă nu este găsit niciun utilizator
  3. Returnează conținut secret după ce se asigură că un utilizator conectat a solicitat acest lucru

Netlify Functions va detecta JWT-urile Netlify Identity în anteturile de autorizare și va pune automat acele informații în context - asta înseamnă că putem verifica dacă există un JWT valid fără a fi nevoie să scriem cod pentru a valida JWT-urile!

Când încărcăm această pagină în browserul nostru, vom vedea mai întâi pagina deconectată:

Vizualizare deconectat care arată informații despre conectare sau crearea unui cont
Când ne-am deconectat, putem vedea doar informații despre conectare. (Previzualizare mare)

Dacă facem clic pe butonul pentru a vă autentifica, vom vedea widgetul Netlify Identity:

O fereastră modală care arată filele de înregistrare și de conectare cu un formular de conectare afișat
Netlify Identity Widget oferă întreaga experiență de conectare/înregistrare. (Previzualizare mare)

După autentificare (sau înregistrare), putem vedea conținutul protejat:

Vizualizare autentificată care arată informații despre o petrecere de naștere
După conectare, putem vedea conținut protejat. (Previzualizare mare)

Wowee! Tocmai am adăugat autentificarea utilizatorului și conținut protejat la o aplicație JAMstack!

Ce sa fac in continuare

JAMstack este mult mai mult decât „doar site-uri statice” – putem răspunde la interacțiunile utilizatorilor, stoca date, gestiona autentificarea utilizatorilor și aproape orice altceva dorim să facem pe un site web modern. Și totul fără a fi nevoie să furnizați, să configurați sau să implementați un server!

Ce vrei să construiești cu JAMstack? Există ceva de care încă nu ești convins că JAMstack poate gestiona? Mi-ar plăcea să aud despre asta - dați-mă pe Twitter sau în comentarii!