JAMstackin’ Divya Sasidharan

Divya Sasidharan @shortdiv Netlify

<> JAM APIs Markup Javascript stack

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

served without web servers. Static

Dynamic

Markup Server

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

DNS Browser CDN … Services

Immutable Deploys Build Automation Event Triggers Serverless

Immutable Deploys Build Automation Event Triggers Serverless

ild Bu a ner Ge te Deploy

html css js md

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

Atomic Deploys

A B

A A

Immutable Deploys Build Automation Event Triggers Serverless

html css js md

md

_ html css js

Immutable Deploys Build Automation Event Triggers Serverless

md html css js

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

Web Hooks

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

Immutable Deploys Build Automation Event Triggers Serverless

DNS Browser CDN … Services

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

Backend Application Frontend Framework … … CDN … Static Site

Frontend Framework … CDN … Static Site

Frontend Framework … CDN …

Functions as a Service

Good News Everyone!

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

Forms Functions Authentication Event triggers ✨

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”

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>

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

Forms Functions Authentication Event triggers ✨

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

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

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

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

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

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

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

Forms Functions Authentication Event triggers ✨

Netlify Identity GoTrue

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

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

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

Forms Functions Authentication Event triggers ✨

The Road to JAMstack

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

Dynamic Static

Thanks! @shortdiv