A presentation at Flashback Conference in in Orlando, FL, USA by Divya
stack
Divya Sasidharan @shortdiv
https://codepen.io/shortdiv/pen/wvawjvX
<> 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 APIs Markup Javascript stack
served without web servers. Static APIs Markup Javascript
served without web servers. Dynamic APIs Markup Javascript
Markup Server
DNS Browser Load Balancer CDN Cloud Storage Caching … Services Web App Server Database Server
DNS Browser CDN … Services
Is this JAMstack?
Pre-rendered Site/SPA Isomorphic SSR SPA Traditional CMS site Monolithic Fullstack App
Immutable Deploys Build Automation Event Triggers Serverless
Immutable Deploys Build Automation Event Triggers Serverless
ild Bu a ner Ge te Deploy
A B
A A
html css js md
Atomic Deploys
This works? f**k I have no idea why this works Why is this not working Work in Progress Initial
Immutable Deploys Build Automation Event Triggers Serverless
`
_ html css js md
html css js md
md html css js
md html css js
html Build md …
Immutable Deploys Build Automation Event Triggers Serverless
Web Hooks
{…} Deploy Site Success CI Webhook {…} Failure Cancel Deploy
Git Activity Split Test Events Build/Deploy Events Form Submissions Content Updates Auth Events
Immutable Deploys Build Automation Event Triggers Serverless
Frontend Framework … Server CDN Browser Static Site … Full Stack Application
Backend Application Frontend Framework … … CDN … Static Site
Frontend Framework … CDN … Static Site
DNS Browser CDN … Services
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
Get to the CDN Design for immutable deploys Automate all the things Serverless all the things
Dynamic Static
JAMuary https://dev.to/t/jamuary
Thanks! @shortdiv
Static sites have made a comeback as of late. This “return to simplicity’ is unsurprising given the current complexity of websites and applications that make maintenance quite the nightmare. Regardless, static sites today are a far cry from their older counterparts. Static sites circa early 2000s consisted of static markup with some CSS thrown in for flavor. Today, static sites are much more dynamic and are a healthy mix of JavaScript, APIs and Markup, aka JAM. They are capable of more complex behavior like processing payments and making authentication requests that are traditionally associated with backend server logic. In this talk, let’s dive into the wonderful world of the JAMstack and how we can harness it to make faster, more secure websites and applications.