Jamstack hits the spot

A presentation at Flashback Conference in February 2020 in Orlando, FL, USA by Divya

Slide 1

Slide 1

stack

Slide 2

Slide 2

Divya Sasidharan @shortdiv

Slide 3

Slide 3

Slide 4

Slide 4

Slide 5

Slide 5

Slide 6

Slide 6

https://codepen.io/shortdiv/pen/wvawjvX

Slide 7

Slide 7

Slide 8

Slide 8

Slide 9

Slide 9

<> JAM APIs Markup Javascript stack

Slide 10

Slide 10

A modern architecture — Create fast and secure sites and dynamic apps with JavaScript, APIs, and prerendered Markup served without web servers. JAM APIs Markup Javascript stack

Slide 11

Slide 11

served without web servers. Static APIs Markup Javascript

Slide 12

Slide 12

served without web servers. Dynamic APIs Markup Javascript

Slide 13

Slide 13

Markup Server

Slide 14

Slide 14

DNS Browser Load Balancer CDN Cloud Storage Caching … Services Web App Server Database Server

Slide 15

Slide 15

DNS Browser CDN … Services

Slide 16

Slide 16

Slide 17

Slide 17

Is this JAMstack?

Slide 18

Slide 18

Pre-rendered Site/SPA Isomorphic SSR SPA Traditional CMS site Monolithic Fullstack App

Slide 19

Slide 19

Immutable Deploys Build Automation Event Triggers Serverless

Slide 20

Slide 20

Immutable Deploys Build Automation Event Triggers Serverless

Slide 21

Slide 21

ild Bu a ner Ge te Deploy

Slide 22

Slide 22

Slide 23

Slide 23

Slide 24

Slide 24

A B

Slide 25

Slide 25

A A

Slide 26

Slide 26

html css js md

Slide 27

Slide 27

Atomic Deploys

Slide 28

Slide 28

This works? f**k I have no idea why this works Why is this not working Work in Progress Initial

Slide 29

Slide 29

Immutable Deploys Build Automation Event Triggers Serverless

Slide 30

Slide 30

Slide 31

Slide 31

`

Slide 32

Slide 32

_ html css js md

Slide 33

Slide 33

html css js md

Slide 34

Slide 34

md html css js

Slide 35

Slide 35

md html css js

Slide 36

Slide 36

html Build md …

Slide 37

Slide 37

Immutable Deploys Build Automation Event Triggers Serverless

Slide 38

Slide 38

Web Hooks

Slide 39

Slide 39

Slide 40

Slide 40

{…} Deploy Site Success CI Webhook {…} Failure Cancel Deploy

Slide 41

Slide 41

Git Activity Split Test Events Build/Deploy Events Form Submissions Content Updates Auth Events

Slide 42

Slide 42

Immutable Deploys Build Automation Event Triggers Serverless

Slide 43

Slide 43

Slide 44

Slide 44

Slide 45

Slide 45

Slide 46

Slide 46

Frontend Framework … Server CDN Browser Static Site … Full Stack Application

Slide 47

Slide 47

Backend Application Frontend Framework … … CDN … Static Site

Slide 48

Slide 48

Frontend Framework … CDN … Static Site

Slide 49

Slide 49

DNS Browser CDN … Services

Slide 50

Slide 50

Frontend Framework … CDN …

Slide 51

Slide 51

Functions as a Service

Slide 52

Slide 52

Good News Everyone!

Slide 53

Slide 53

emo Time! Demo Time! Demo Tim emo Time! Demo Time! Demo Tim emo Time! Demo Time! Demo Tim emo Time! Demo Time! Demo Tim emo Time! Demo Time! Demo Tim emo Time! Demo Time! Demo Tim

Slide 54

Slide 54

Slide 55

Slide 55

Slide 56

Slide 56

Forms Functions Authentication Event triggers ✨

Slide 57

Slide 57

Slide 58

Slide 58

Slide 59

Slide 59

1 <form 2 name=“delivery-request“ 3 method=“POST” 4 netlify-honeypot=“bot-field” 5 data-netlify=“true” 6 > 7 <p class=”hidden”> 8 <label> 9 Don’t fill this out if you’re human: 10 <input name=”bot-field” /> 11 </label> 12 </p> 13 <p> 14 <label>Contents: 15 <input type=”text” name=”contents” /> 16 </label> 17 </p> 18 <fieldset> 19 <label> 20 <input 21 name=”chosenCrew” 22 type=”checkbox” 23 /> 24 <span>Leela</span> 25 </label> 26 <label> 27 <input 28 name=”chosenCrew”

Slide 60

Slide 60

1 <template> 2 <div class=”hello”> 3 <form 4 name=”delivery-request” 5 method=”post” 6 data-netlify=”true” 7 data-netlify-honeypot=”bot-field” 8 @submit.prevent=”handleSubmit” 9 > 10 <header> 11 <h2>Planet Express Delivery Request</h2> 12 </header> 13 <input type=”hidden” name=”form-name” value=”delivery-request” / 14 <input type=”hidden” id=”bot” name=”bot” /> 15 <div> 16 <label> 17 <span>Contents</span> 18 <input 19 id=”contents” 20 name=”contents” 21 type=”text” 22 v-model=”form.contents” 23 /> 24 </label> 25 … 26 </div> 27 <button type=”submit” class=”submit-button”>Request Job</button> 28 </form>

Slide 61

Slide 61

}; 46 }, 47 methods: { 48 encode(data) { 49 return Object.keys(data) 50 .map( 51 key => `${encodeURIComponent(key)}=${encodeURIComponent(data 52 ) 53 .join(“&”); 54 }, 55 handleSubmit() { 56 const axiosConfig = { 57 header: { “Content-Type”: “application/x-www-form-urlencoded” 58 }; 59 const payload = { 60 “form-name”: “delivery-request”, 61 …this.form 62 }; 63 axios 64 .post(“/”, this.encode(payload), axiosConfig) 65 .then(() => { 66 this.$router.push(“thanks”); 67 }) 68 .catch(() => { 69 this.$router.push(“404”); 70 }); 71 } 72 } 73 }; 74 </script>

Slide 62

Slide 62

Forms Functions Authentication Event triggers ✨

Slide 63

Slide 63

Slide 64

Slide 64

Slide 65

Slide 65

Slide 66

Slide 66

exports.handler = function(event, context, callback) { console.log(event) callback(null, { statusCode: 200, body: JSON.stringify({ msg: “Hello, World!” }) }) } functions/hello.js

Slide 67

Slide 67

exports.handler = function(event, context, callback) { console.log(event) callback(null, { statusCode: 200, body: JSON.stringify({ msg: “Hello, World!” }) }) } functions/submission-created.js

Slide 68

Slide 68

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 /* Triggered when a form submission is posted to your site. */ const faunadb = require(“faunadb”); const q = faunadb.query; const client = new faunadb.Client({ secret: process.env.FAUNA_SECRET } exports.handler = (event, context, callback) => { const body = JSON.parse(event.body).payload; const { contents, chosenCrew, recipient, destination } = body.data; client .query( q.Create(q.Class(“deliveries”), { data: { Contents: contents, Crew: chosenCrew.split(“,”), Recipient: recipient, Destination: destination, status: “pending” } }) ) .then(ret => { console.log(ret);

Slide 69

Slide 69

6 7 exports.handler = (event, context, callback) => { 8 const body = JSON.parse(event.body).payload; 9 const { 10 contents, 11 chosenCrew, 12 recipient, 13 destination 14 } = body.data; 15 client 16 .query( 17 q.Create(q.Class(“deliveries”), { 18 data: { 19 Contents: contents, 20 Crew: chosenCrew.split(“,”), 21 Recipient: recipient, 22 Destination: destination, 23 status: “pending” 24 } 25 }) 26 ) 27 .then(ret => { 28 console.log(ret); 29 return callback(null, { 30 statusCode: 200, 31 body: JSON.stringify(body) 32 }); 33 }) 34 .catch(err => { 35 return callback(null, {

Slide 70

Slide 70

6 7 exports.handler = (event, context, callback) => { 8 const body = JSON.parse(event.body).payload; 9 const { 10 contents, 11 chosenCrew, 12 recipient, 13 destination 14 } = body.data; 15 client 16 .query( 17 q.Create(q.Class(“deliveries”), { 18 data: { 19 Contents: contents, 20 Crew: chosenCrew.split(“,”), 21 Recipient: recipient, 22 Destination: destination, 23 status: “pending” 24 } 25 }) 26 ) 27 .then(ret => { 28 console.log(ret); 29 return callback(null, { 30 statusCode: 200, 31 body: JSON.stringify(body) 32 }); 33 }) 34 .catch(err => { 35 return callback(null, {

Slide 71

Slide 71

6 7 exports.handler = (event, context, callback) => { 8 const body = JSON.parse(event.body).payload; 9 const { 10 contents, 11 chosenCrew, 12 recipient, 13 destination 14 } = body.data; 15 client 16 .query( 17 q.Create(q.Class(“deliveries”), { 18 data: { 19 Contents: contents, 20 Crew: chosenCrew.split(“,”), 21 Recipient: recipient, 22 Destination: destination, 23 status: “pending” 24 } 25 }) 26 ) 27 .then(ret => { 28 console.log(ret); 29 return callback(null, { 30 statusCode: 200, 31 body: JSON.stringify(body) 32 }); 33 }) 34 .catch(err => { 35 return callback(null, {

Slide 72

Slide 72

6 7 exports.handler = (event, context, callback) => { 8 const body = JSON.parse(event.body).payload; 9 const { 10 contents, 11 chosenCrew, 12 recipient, 13 destination 14 } = body.data; 15 client 16 .query( 17 q.Create(q.Class(“deliveries”), { 18 data: { 19 Contents: contents, 20 Crew: chosenCrew.split(“,”), 21 Recipient: recipient, 22 Destination: destination, 23 status: “pending” 24 } 25 }) 26 ) 27 .then(ret => { 28 console.log(ret); 29 return callback(null, { 30 statusCode: 200, 31 body: JSON.stringify(body) 32 }); 33 }) 34 .catch(err => { 35 return callback(null, {

Slide 73

Slide 73

Forms Functions Authentication Event triggers ✨

Slide 74

Slide 74

Slide 75

Slide 75

Netlify Identity GoTrue

Slide 76

Slide 76

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import Vue from ‘vue’ import Vuex from ‘vuex’ import GoTrue from “gotrue-js”; Vue.use(Vuex) const auth = new GoTrue({ APIUrl: “https://planet-express-deliveries.netlify.com/.netlify/iden audience: “”, setCookie: false }); export default new Vuex.Store({ state: { currentUser: getSavedState(“auth.currentUser”), loggedIn: false, deliveries: [] }, mutations: { SET_CURRENT_USER(state, value) { state.currentUser = value; } }, getters: { loggedIn(state) { return !!state.currentUser; } },

Slide 77

Slide 77

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 }, attemptLogin({ commit, dispatch }, credentials) { return new Promise((resolve, reject) => { auth .login(credentials.email, credentials.password) .then(response => { resolve(response); commit(“SET_CURRENT_USER”, response); }) .catch(error => { reject(error.json); }); }); }, attemptLogout({ commit }) { return new Promise((resolve, reject) => { const user = auth.currentUser(); user .logout() .then(response => { console.log(response); resolve(response); commit(“SET_CURRENT_USER”, null); }) .catch(error => { reject(error); console.log(“Could not log out”, error); }); }); },

Slide 78

Slide 78

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 }, attemptLogin({ commit, dispatch }, credentials) { return new Promise((resolve, reject) => { auth .login(credentials.email, credentials.password) .then(response => { resolve(response); commit(“SET_CURRENT_USER”, response); }) .catch(error => { reject(error.json); }); }); }, attemptLogout({ commit }) { return new Promise((resolve, reject) => { const user = auth.currentUser(); user .logout() .then(response => { console.log(response); resolve(response); commit(“SET_CURRENT_USER”, null); }) .catch(error => { reject(error); console.log(“Could not log out”, error); }); }); },

Slide 79

Slide 79

Forms Functions Authentication Event triggers ✨

Slide 80

Slide 80

Slide 81

Slide 81

The Road to

Slide 82

Slide 82

Get to the CDN Design for immutable deploys Automate all the things Serverless all the things

Slide 83

Slide 83

Dynamic Static

Slide 84

Slide 84

JAMuary https://dev.to/t/jamuary

Slide 85

Slide 85

Thanks! @shortdiv