A presentation at Fauna DB All Hands in in Boston, MA, USA by Divya
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