The Quest for Serverless Divya Sasidharan

HELLO MY NAME IS Hellot Divya Sasidharan

HELLO MY NAME IS Divya Sasidharan Hellot @shortdiv

The Ordinary World The call to adventure Refusal Meeting the Mentor Crossing the threshold Tests, Allies, Enemies Innermost Cave Ordeal Reward The road back Resurrection Return with the Elixir

The Ordinary World The call to adventure Refusal Meeting the Mentor Crossing the threshold Tests, Allies, Enemies Innermost Cave Ordeal Reward The road back Resurrection Return with the Elixir DEPARTURE

The Ordinary World The call to adventure Refusal DEPARTURE Meeting the Mentor Crossing the threshold Tests, Allies, Enemies Innermost Cave Ordeal Reward The road back Resurrection Return with the Elixir INITIATION

The Ordinary World The call to adventure Refusal DEPARTURE Meeting the Mentor Crossing the threshold Tests, Allies, Enemies Innermost Cave INITIATION Ordeal Reward The road back Resurrection Return with the Elixir RETURN

Act 1 Departure

The Ordinary World DATE SCENE ACT 07/09 1 1

The Ordinary World DATE SCENE ACT 07/09 1 1

The Ordinary World DATE SCENE ACT 07/09 1 1

✅ Sammy Serverus

bon appetite Spatchcocked Chicken The best recipe for those of you living under a rock

Call to Adventure DATE SCENE ACT 07/09 2 1

Call to Adventure DATE SCENE ACT 07/09 2 1

Call to Adventure DATE SCENE ACT 07/09 2 1

Orange News 1. Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments 2. Some news (github.com) 100000 points by somedude 3 hours ago | hide | 1 comments 3. Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments 4. Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments 5. Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments 6. 7. 8. 9. 10. 11. 12. 13. Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments 14. Some news (github.com) 100000 points by somedude 3 hours ago | hide | 100000 comments 15. Some news (github.com)

got-lobstah.com

How ‘bout that “JAM“ I’ll lose the bleeps, the sweeps and all those creeps

Refusal DATE SCENE ACT 07/09 3 1

Refusal DATE SCENE ACT 07/09 3 1

Refusal DATE SCENE ACT 07/09 3 1

We’re gonna need a bigger server rack

😩😩😩😩

Meeting the Mentor DATE SCENE ACT 07/09 4 1

Meeting the Mentor DATE SCENE ACT 07/09 4 1

Meeting the Mentor DATE SCENE ACT 07/09 4 1

Swami Scale-a-lot

Man who run servers without servers, accomplish many things

Crossing the Threshold DATE SCENE ACT 07/09 5 1

Crossing the Threshold DATE SCENE ACT 07/09 5 1

Crossing the Threshold DATE SCENE ACT 07/09 5 1

Act 2 Initiation

Tests, Allies, Enemies DATE SCENE ACT 07/09 1 2

Tests, Allies, Enemies DATE SCENE ACT 07/09 1 2

Tests, Allies, Enemies DATE SCENE ACT 07/09 1 2

|—————-| | THERE | | ARE | | STILL | | SERVERS | | IN | | SERVERLESS| |—————-| (__/) || (•ㅅ•) || / づ

How ‘bout that “Cold Start“?

Innermost Cave DATE SCENE ACT 07/09 2 2

Innermost Cave DATE SCENE ACT 07/09 2 2

Innermost Cave DATE SCENE ACT 07/09 2 2

1 const express = require(‘express’), yelp = require(‘yelp-fusion’), 2 port = 8000, 3 app = express(); 4 5 6 app.use((req, res, next) => { res.header(“Access-Control-Allow-Origin”, “*”); 7 res.header(“Access-Control-Allow-Headers”, “Origin, X-Requested 8 -with, Content-Type, Accept”); 9 next(); 10 }) 11 12 13 app.get(‘/yelpit/:term/:location’, (req, res) => { const clientId = CLIENT_ID; 14 const clientSecret = CLIENT_SECRET; 15 16 yelp.accessToken(clientId, clientSecret) 17 .then((response) => { 18 const client = yelp.client(response.jsonBody.access_token); 19 const searchRequest = { 20 term: req.params.term, 21 limit: 50, 22 location: req.params.location 23 } 24 client.search(searchRequest).then(response => { 25 const firstResult = response.jsonBody.businesses; 26 res.send(firstResult) 27

1 const express = require(‘express’), yelp = require(‘yelp-fusion’), 2 port = 8000, 3 app = express(); 4 5 6 app.use((req, res, next) => { res.header(“Access-Control-Allow-Origin”, “*”); 7 res.header(“Access-Control-Allow-Headers”, “Origin, X-Requested 8 -with, Content-Type, Accept”); 9 next(); 10 }) 11 12 13 app.get(‘/yelpit/:term/:location’, (req, res) => { const clientId = CLIENT_ID; 14 const clientSecret = CLIENT_SECRET; 15 16 yelp.accessToken(clientId, clientSecret) 17 .then((response) => { 18 const client = yelp.client(response.jsonBody.access_token); 19 const searchRequest = { 20 term: req.params.term, 21 limit: 50, 22 location: req.params.location 23 } 24 client.search(searchRequest).then(response => { 25 const firstResult = response.jsonBody.businesses; 26 res.send(firstResult) 27

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 }) app.get(‘/yelpit/:term/:location’, (req, res) => { const clientId = CLIENT_ID; const clientSecret = CLIENT_SECRET; }) yelp.accessToken(clientId, clientSecret) .then((response) => { const client = yelp.client(response.jsonBody.access_token); const searchRequest = { term: req.params.term, limit: 50, location: req.params.location } client.search(searchRequest).then(response => { const firstResult = response.jsonBody.businesses; res.send(firstResult) }) }) app.listen(port, ‘localhost’, function(err) { if(err) {console.log(err)} console.info(Listening on port ${port}) });

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 module.exports = { devServer: { proxy: { ‘^/yelpit/*’: { target: ‘http://localhost:8000/yelpit’, secure: false } } } }

The Chilling Adventures of Functions as a Service |—————-| | THERE | | ARE | | STILL | | SERVERS | | IN | | SERVERLESS| |—————-| (_/) || (•ㅅ•) || / づ |—————-| | THERE | | ARE | | STILL | | SERVERS | | IN | | SERVERLESS| |—————-| (_/) || (•ㅅ•) || / づ |—————-| | THERE | | ARE | | STILL | | SERVERS | | IN | | SERVERLESS| |—————-| (_/) || (•ㅅ•) || / づ |—————-| | THERE | | ARE | | STILL | | SERVERS | | IN | | SERVERLESS| |—————-| (_/) || (•ㅅ•) || / づ

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

[build] command = “yarn build” functions = “functions” publish = “dist” netlify.toml

/.netlify/functions/yelp-it

1 const yelp = require(“yelp-fusion”); 2 3 exports.handler = async event => { 4 const client = yelp.client(process.env.YELP_API_KEY); 5 const { term, location } = event.queryStringParameters; 6 const searchRequest = { 7 term: term, 8 location: location 9 }; 10 var geojsonify = async res => { 11 let features = []; 12 res.map(item => { 13 features.push({ 14 type: “Feature”, 15 geometry: { 16 type: “Point”, 17 coordinates: [item.coordinates.longitude, item.coordinates.l 18 }, 19 properties: { 20 …item 21 22 } 23 }); 24 }); 25 return { 26 type: “FeatureCollection”, 27 features 28 }; 29 };

coordinates: [item.coordinates.longitude, item.coordinates.l 16 }, 17 properties: { 18 …item 19 } 20 }); 21 }); 22 return { 23 type: “FeatureCollection”, 24 25 features 26 }; 27 }; 28 try { 29 let res = await client.search(searchRequest); 30 let geojsonified = await geojsonify(res.jsonBody.businesses); 31 return { 32 statusCode: 200, 33 body: JSON.stringify({ 34 results: geojsonified 35 }) 36 }; 37 } catch (err) { 38 return { statusCode: 500, body: err.toString() }; 39 } 40 41 }; 42 43 44

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 <template> <div class=“home”> <div id=”sidebar” v-if=”dataLoaded”> <Slider :yelpData=“yelpData.features” /> </div> </div> </template>

<script> import { value, onCreated } from “vue-function-api”; export default { name: “home”, setup() { const yelpData = value(null); const getYelpData = () => { axios .get(“/.netlify/functions/yelp-it”, { params: { location: “boston, ma”, term: “lobster” } }) .then(async res => { yelpData.value = res.data.results;

14 export default { name: “home”, 15 setup() { 16 const yelpData = value(null); 17 const getYelpData = () => { 18 axios 19 .get(“/.netlify/functions/yelp-it”, { 20 params: { 21 location: “boston, ma”, 22 term: “lobster” 23 } 24 }) 25 .then(async res => { 26 yelpData.value = res.data.results; 27 }) 28 .catch(err => { 29 console.log(err); 30 }); 31 }; 32 onCreated(async () => { 33 getYelpData(); 34 }); 35 } 36 37 } 38 </script> 39 40

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 <template> <div class=“home”> <div id=”sidebar” v-if=”dataLoaded”> <Slider :yelpData=“yelpData.features” /> </div> </div> </template>

<script> import { value, onCreated } from “vue-function-api”; export default { name: “home”, setup() { const yelpData = value(null); const getYelpData = () => { axios .get(“/.netlify/functions/yelp-it”, { params: { location: “boston, ma”, term: “lobster” } }) .then(async res => { yelpData.value = res.data.results;

netlify addons:create fauna

1 const faunadb = require(“faunadb”); 2 const q = faunadb.query; 3 4 export default async function useFauna() { const createClient = async () => { 5 const secret = await axios.get(“/.netlify/functions/fauna-user” 6 const { faunaSecret } = secret.data; 7 const client = new faunadb.Client({ secret: faunaSecret }); 8 return client; 9 }; 10 11 const client = await createClient(); 12 13 const setRating = ref => async title => { 14 try { 15 if (ref === null) { 16 client 17 .query( 18 q.Create(q.Class(“ratings”), { 19 data: title 20 }) 21 ) 22 .then(ret => console.log(ret)) 23 .catch(err => console.log(err)); 24 } 25 const listicle = await client 26

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 const client = await createClient(); const setRating = ref => async title => { try { if (ref === null) { client .query( q.Create(q.Class(“ratings”), { data: title }) ) .then(ret => console.log(ret)) .catch(err => console.log(err)); } else { client .query( q.Update(q.Ref(q.Class(“ratings”), ref), { data: title }) ) .then(ret => console.log(ret)); } return listicle; } catch (err) { console.log(err); } };

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 console.log(err); } }; const getRatings = async () => { try { const res = await client.query( q.Map(q.Paginate(q.Match(q.Ref(“indexes/all_ratings”))), ref => q.Get(ref) ) ); return res.data; } catch (err) { console.log(err); } }; return { client, getRatings, setRating };

Ordeal DATE SCENE ACT 07/09 3 2

Ordeal DATE SCENE ACT 07/09 3 2

Ordeal DATE SCENE ACT 07/09 3 2

Auth as a Service

Netlify Identity

1 const netlifyIdentity = require(“netlify-identity-widget”); C 2 3 export default function useAuth() { 4 const netlifyIdentityInit = () => { 5 return netlifyIdentity.init({ 6 container: “.account-profile” C 7 }); 8 }; 9 10 netlifyIdentityInit(); 11 12 return { 13 netlifyIdentity 14 15 }; 16 }; 17 18 19 20 21 22 23 24 25 26 27 28

/.netlify/functions/handle-signin

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 ‘use strict’; const faunadb = require(‘faunadb’); C const generator = require(‘generate-password’); /* configure faunaDB Client with our secret / const q = faunadb.query; C const client = new faunadb.Client({ secret: process.env.FAUNADB_SERVER_SECRET C }); / create a user in FaunaDB that can connect from the browser */ function createUser(userData, password) { return client.query( q.Create(q.Class(‘users’), { credentials: { password: password }, data: { id: userData.id, user_metadata: userData.user_metadata } }) ); } function obtainToken(user, password) { return client.query(q.Login(q.Select(‘ref’, user), { password })); }

return client.query(q.Login(q.Select(‘ref’, user), { password })); 29 } 30 31 export function handler(event, context, callback) { C 32 var payload = JSON.parse(event.body); C 33 var userData = payload.user; C 34 35 const password = generator.generate({ 36 length: 10, 37 C numbers: true 38 }); 39 40 createUser(userData, password)C 41 .then(user => obtainToken(user, password)) C 42 .then(key => 43 callback(null, { 44 statusCode: 200, 45 body: JSON.stringify({ 46 app_metadata: { 47 faunadb_token: key.secret C 48 } 49 }) 50 }) 51 ) 52 .catch(e => { 53 console.error(e); 54 callback(null, { 55 statusCode: 500, 56 body: JSON.stringify({ 57 error: e

Reward DATE SCENE ACT 07/09 4 2

Reward DATE SCENE ACT 07/09 4 2

Reward DATE SCENE ACT 07/09 4 2

JAMstack ecosystem

Form Handling Search Push Notifications Authentication Database Static Hosting Payments Media Storage Headless CMS Monitoring FAAS Providers JAMstack ecosystem

secure fast flexible scalable

Act 3 Return

The Road Back DATE SCENE ACT 07/09 1 3

The Road Back DATE SCENE ACT 07/09 1 3

The Road Back DATE SCENE ACT 07/09 1 3

There is no cloud, just someone else’s server

Resurrection/ Return with Elixir DATE SCENE ACT 07/09 2 3

Resurrection/ Return with Elixir DATE SCENE ACT 07/09 2 3

Resurrection/ Return with Elixir DATE SCENE ACT 07/09 2 3

thepowerofserverless.info

all the things

👏 👏 👏 👏 👏 👏 ⚡ ⚡ ⚡ 👏 👏 👏 👏

gotlobstah.netlify.com

% ❤ github.com/shortdiv/got-lobstah