Jamstackin’

A presentation at Artifact in September 2019 in Austin, TX, USA by Divya

Slide 1

Slide 1

JAMstackin’ Divya Sasidharan

Slide 2

Slide 2

Divya Sasidharan @shortdiv Netlify

Slide 3

Slide 3

Slide 4

Slide 4

Slide 5

Slide 5

Slide 6

Slide 6

Slide 7

Slide 7

Slide 8

Slide 8

Slide 9

Slide 9

Slide 10

Slide 10

<> JAM APIs Markup Javascript stack

Slide 11

Slide 11

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

Slide 12

Slide 12

served without web servers. Static

Slide 13

Slide 13

Dynamic

Slide 14

Slide 14

Markup Server

Slide 15

Slide 15

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

Slide 16

Slide 16

DNS Browser CDN … Services

Slide 17

Slide 17

Slide 18

Slide 18

Immutable Deploys Build Automation Event Triggers Serverless

Slide 19

Slide 19

Immutable Deploys Build Automation Event Triggers Serverless

Slide 20

Slide 20

ild Bu a ner Ge te Deploy

Slide 21

Slide 21

Slide 22

Slide 22

html css js md

Slide 23

Slide 23

This works? Jen told me not to swear but f**k I have no idea why this works Why is this not working Work in Progress Initial

Slide 24

Slide 24

Atomic Deploys

Slide 25

Slide 25

Slide 26

Slide 26

A B

Slide 27

Slide 27

A A

Slide 28

Slide 28

Immutable Deploys Build Automation Event Triggers Serverless

Slide 29

Slide 29

Slide 30

Slide 30

html css js md

Slide 31

Slide 31

md

_ html css js

Slide 32

Slide 32

Immutable Deploys Build Automation Event Triggers Serverless

Slide 33

Slide 33

md html css js

Slide 34

Slide 34

Slide 35

Slide 35

{…} Process Payment Success webhooks {…} Failure Cancel Order

Slide 36

Slide 36

Web Hooks

Slide 37

Slide 37

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

Slide 38

Slide 38

Immutable Deploys Build Automation Event Triggers Serverless

Slide 39

Slide 39

DNS Browser CDN … Services

Slide 40

Slide 40

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

Slide 41

Slide 41

Backend Application Frontend Framework … … CDN … Static Site

Slide 42

Slide 42

Frontend Framework … CDN … Static Site

Slide 43

Slide 43

Frontend Framework … CDN …

Slide 44

Slide 44

Functions as a Service

Slide 45

Slide 45

Slide 46

Slide 46

Good News Everyone!

Slide 47

Slide 47

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 48

Slide 48

Slide 49

Slide 49

Slide 50

Slide 50

Slide 51

Slide 51

Forms Functions Authentication Event triggers ✨

Slide 52

Slide 52

Slide 53

Slide 53

Slide 54

Slide 54

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 55

Slide 55

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 56

Slide 56

}; 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 57

Slide 57

Forms Functions Authentication Event triggers ✨

Slide 58

Slide 58

Slide 59

Slide 59

Slide 60

Slide 60

Slide 61

Slide 61

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

Slide 62

Slide 62

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

Slide 63

Slide 63

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 64

Slide 64

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 65

Slide 65

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 66

Slide 66

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 67

Slide 67

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 68

Slide 68

Forms Functions Authentication Event triggers ✨

Slide 69

Slide 69

Slide 70

Slide 70

Netlify Identity GoTrue

Slide 71

Slide 71

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 72

Slide 72

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 73

Slide 73

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 74

Slide 74

Forms Functions Authentication Event triggers ✨

Slide 75

Slide 75

Slide 76

Slide 76

The Road to JAMstack

Slide 77

Slide 77

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

Slide 78

Slide 78

Dynamic Static

Slide 79

Slide 79

Thanks! @shortdiv