Serverless Functions and Vue.js

A presentation at Frontend Developer Love in February 2018 in Amsterdam, Netherlands by Sarah Drasner

Slide 1

Slide 1

SERVERLESS FUNCTIONS AND VUE.JS

Slide 2

Slide 2

Slide 3

Slide 3

SUCCESS!

Slide 4

Slide 4

Slide 5

Slide 5

Slide 6

Slide 6

NOT FAIL…

Slide 7

Slide 7

Slide 8

Slide 8

SERVERLESS AN ACTUALLY INTERESTING THING WITH A CLICKBAITY TITLE FaaS FUNCTIONS AS A SERVICE

Slide 9

Slide 9

SARAH DRASNER @SARAH_EDO

Slide 10

Slide 10

BENEFITS • You pay only for what you use (usually a lot less) • Less time babysitting a server • Same idea as functional programming, breaking things into small bits • Principle of least power

Slide 11

Slide 11

LINK TO CODEPEN

Slide 12

Slide 12

LINK TO CODEPEN

Slide 13

Slide 13

SERVERLESS ALL THE THINGS? NO, NOT REALLY.

Slide 14

Slide 14

SO WHAT IS IT GOOD FOR? • Clean data on a cron job ⏰

• Take data and use it to create data visualizations "

• Crop images uploaded by the user and create a user profile

Slide 15

Slide 15

Consumption Plan Free account signup

Slide 16

Slide 16

Slide 17

Slide 17

VUE.JS, STRIPE, AND SERVERLESS FIRST USE CASE

Slide 18

Slide 18

SAMPLE VUE SHOP VISIT THE SITE

Slide 19

Slide 19

THE APPLICATION VISIT THE REPO

Slide 20

Slide 20

UNDER API

Slide 21

Slide 21

app.get( '/' , (req, res) => res.render( 'index.pug' , { keyPublishable })); app.post( '/charge' , (req, res) => {

let amount = 500 ; stripe.customers .create({ email: req.body.stripeEmail, source: req.body.stripeToken }) .then(customer => stripe.charges.create({ amount, description: 'Sample Charge' , currency: 'usd' , customer: customer.id }) ) .then(charge => res.render( 'charge.pug' )); }); app.listen( 4567 ); What Stripe expects: Express

Slide 22

Slide 22

Where we’re going we don’t need Express!

Slide 23

Slide 23

Kick things off! var stripe = require ( ‘stripe' )( 'sk_test_XXXXXXXXXXXXXXXX' ); // ^ this is a stripe testing key module .exports = function (context, req) { context.log( 'starting to get down' );

Slide 24

Slide 24

If we have what we need,

create the customer and then the charge if ( req.body && req.body.stripeEmail && req.body.stripeToken && req.body.stripeAmt ){ stripe.customers .create({ email: req.body.stripeEmail, source: req.body.stripeToken }) .then(customer => { context.log( 'starting the stripe charges' ); stripe.charges.create({ amount: req.body.stripeAmt, description: 'Sample Charge' , currency: 'usd' , customer: customer.id }); }) ...

Slide 25

Slide 25

Then finish, otherwise error logs ... .then(charge => { context.log( 'finished the stripe charges' ); context.res = {

// status: 200 body: 'This has been completed' }; context.done(); }) .catch(err => { context.log(err); context.done(); }); } else { context.log(req.body); context.res = { status: 400 , body: "We're missing something" }; context.done(); } };

Slide 26

Slide 26

Slide 27

Slide 27

SUCCESS! POST ON CSS-TRICKS

Slide 28

Slide 28

Slide 29

Slide 29

Slide 30

Slide 30

OUR CART VUEX HAS A MANIFEST OF ALL OF OUR PRODUCTS, ITEMS, AND TOTAL

Slide 31

Slide 31

< div v-if= "cartTotal > 0"

<!--we'll add our checkout here-->

</ div

< div v-else-if= "cartTotal === 0 && success === false" class= "empty"

<!--we'll add our empty state here-->

</ div

< div v-else>

<!--we'll add success here-->

</ div

Cart.vue Base Logic

Slide 32

Slide 32

OUR CART < div v-if= "cartTotal > 0"

< h1

Cart </ h1

< div class= "cartitems" v-for= "item in cart" key= "item"

< div class= "carttext"

< h4

{{ item.name }} </ h4

< p

{{ item.price | usdollar }} x {{ item.count }} </ p

< p

Total for this item: < strong

{{ item.price * item.count }} </ strong

</ p

</ div

< img class= "cartimg" :src= "/${item.img}" :alt= "Image of ${item.name}"

</ div

< div class= "total"

< h3

Total: {{ total | usdollar }} </ h3

</ div

<!--we're going to add our checkout here-->

</ div

Slide 33

Slide 33

computed: { ... total() {

return

Object .values( this .cart) .reduce((acc, el) => acc + (el.count * el.price), 0 ) .toFixed( 2 ); } }

Slide 34

Slide 34

Slide 35

Slide 35

VUE STRIPE CHECKOUT EASIEST WAY REPO

Slide 36

Slide 36

STRIPE ELEMENTS WE’LL USE:

Slide 37

Slide 37

OR npm i vue-stripe-elements-plus yarn add vue-stripe-elements-plus

Slide 38

Slide 38

Slide 39

Slide 39

DEFAULT STRIPE-ELEMENT < template

< div id= 'app'

< h1

Please give us your payment details: </ h1

< card class= 'stripe-card' :class= '{ complete }' stripe= 'pk_test_XXXXXXXXXXXXXXXXXXXXXXXX' :options= 'stripeOptions' @change= 'complete = $event.complete' />

< button class= 'pay-with-stripe'

  @click=

'pay'

  :disabled=

'!complete' > Pay with credit card

</ button

</ div

</ template

Slide 40

Slide 40

DEFAULT STRIPE-ELEMENT import { stripeKey, stripeOptions } from

'./stripeConfig.json' import { Card, createToken } from

'vue-stripe-elements-plus' export

default { data () {

return { complete: false , stripeOptions: {

// see https://stripe.com/docs/stripe.js#element-options for details } } },

components: { Card }, methods: { pay() {

// createToken returns a Promise which resolves in a result object with

// either a token or an error key. createToken().then(data => console .log(data.token)) } } }

Slide 41

Slide 41

WHAT WE’LL NEED: data() {

return { submitted: false , complete: false , status: '' , response: '' , stripeOptions: {

// you can configure that cc element. }, stripeEmail: '' } },

Slide 42

Slide 42

WHAT WE’LL NEED: props: { total: { type: [ Number , String ], default: '50.00' }, success: { type: Boolean , default: false } },

Slide 43

Slide 43

HERE WE GO! pay() { createToken().then(data => {

// we'll change this to know it was submitted

this .submitted = true ;

// this is a token we would use for the stripeToken below

console .log(data.token); axios .post(

'https://sdras-stripe.azurewebsites.net/api/charge? code=zWwbn6LLqMxuyvwbWpTFXdRxFd7a27KCRCEseL7zEqbM9ijAgj1c1w==' , { stripeEmail: this .stripeEmail, // send the email stripeToken: data.token.id, // testing token stripeAmt: this .total // send the amount }, { headers: {

'Content-Type' : 'application/json' } } ) ...

Slide 44

Slide 44

Slide 45

Slide 45

HERE WE GO! ... .then(response => {

this .status = 'success' ;

this .$emit( 'successSubmit' );

this .$store.commit( 'clearCartCount' );

// console logs for you :)

this .response = JSON .stringify(response, null , 2 );

console .log( this .response); }) .catch(error => {

this .status = 'failure' ;

// console logs for you :)

this .response = 'Error: ' + JSON .stringify(error, null , 2 );

console .log( this .response); }); });

Slide 46

Slide 46

pay() • Uses Axios to post to our function to our function URL • Tracks whether we've submitted the form or not, with this.submitted

• It sends the email, token, and total to our serverless function • If it's successful, commits to our Vuex store, mutation clears our cart, and emits to the parent cart component that the payment was successful.  

• If it errors, it changes the status to failure, and logs the error response for help with debugging

Slide 47

Slide 47

WHILE THE FUNCTION WORKS ITS MAGIC ✨ LINK TO CODEPEN

Slide 48

Slide 48

IN THE CASE OF FAILURE

Slide 49

Slide 49

Template Script < div v-if= "status === 'failure'"

< h3

Oh No! </ h3

< p

Something went wrong! </ p

< button @click= "clearCheckout"

Please try again </ button

</ div

clearCheckout() {

this .submitted = false ;

this .status = '' ;

this .complete = false ;

this .response = '' ; }

Slide 50

Slide 50

IN THE CASE OF SUCCESS

Slide 51

Slide 51

Slide 52

Slide 52

Slide 53

Slide 53

AppSuccess.vue window .setTimeout(() => this .$emit( 'restartCart' ), 3000 ); THIS CODEPEN

Slide 54

Slide 54

< div v-if= "cartTotal > 0"

<!--we'll add our checkout here-->

</ div

< div v-else-if= "cartTotal === 0 && success === false" class= "empty"

<!--we'll add our empty state here-->

</ div

< div v-else>

<!--we'll add success here-->

</ div

Slide 55

Slide 55

< div v-if= "cartTotal > 0"

< h1

Cart </ h1

...

< app-checkout :total= "total" @successSubmit= "success = true"

</ app-checkout

</ div

< div v-else-if= "cartTotal === 0 && success === false" class= "empty"

< h1

Cart </ h1

< h3

Your cart is empty. </ h3

< nuxt-link exact to= "/"

< button

Fill 'er up! </ button

</ nuxt-link

</ div

< div v-else>

< app-success @restartCart= "success = false" />

< h2

Success! </ h2

< p

Your order has been processed, it will be delivered shortly. </ p

</ div

Slide 56

Slide 56

Slide 57

Slide 57

SECOND USE CASE LET’S GET VISUAL GOOGLE MAPS API POWERED DATA VIS

Slide 58

Slide 58

Series:   Exploring data with Serverless and Vue DEMO   AND   REPO W/ OSS CODE

Slide 59

Slide 59

[
{

" Name " : " Simona Cotin " ,

" Conference " : " DLD 2017 Tel Aviv Israel " ,

" From " : " 9/4/2017 " ,

" To " : " 9/7/2017 " ,

" Location " : " Tel Aviv " ,

" Link " : “"

},

...

]

What we start with looks like this

Slide 60

Slide 60

MAKE THE FUNCTION

Slide 61

Slide 61

getGeo(makeIterator(content), (updatedContent, err) => {

if (!err) {

// we need to base64 encode the JSON to embed it into the PUT (dear god, why)

let updatedContentB64 = new Buffer(

JSON .stringify(updatedContent, null , 2 ) ).toString( 'base64' );

let pushData = { path: GH_FILE, message: 'Looked up locations, beep boop.' , content: updatedContentB64, sha: data.sha }; ... }; We're going to retrieve the geo-information for each item in the original data

Slide 62

Slide 62

function

getGeo (itr, cb) {

let curr = itr.next();

if (curr.done) {

// All done processing- pass the (now-populated) entries to the next callback cb(curr.data);

return ; }

let location = curr.value.Location;

Slide 63

Slide 63

Slide 64

Slide 64

[
{

" Name " : " Simona Cotin " ,

" Conference " : " DLD 2017 Tel Aviv Israel " ,

" From " : " 9/4/2017 " ,

" To " : " 9/7/2017 " ,

" Location " : " Tel Aviv " ,

" Link " : “"

},

...

]

The function updates our cda-data.json… …which we pull into our Vuex store

Slide 65

Slide 65

[
{

" Name " : " Simona Cotin " ,

" Conference " : " DLD 2017 Tel Aviv Israel " ,

" From " : " 9/4/2017 " ,

" To " : " 9/7/2017 " ,

" Location " : " Tel Aviv " ,

" Link " : "" ,

" Latitude " : 32.0852999 ,

" Longitude " : 34.78176759999999

},

...

]

The function updates our cda-data.json… …which we pull into our Vuex store

Slide 66

Slide 66

computed: {

speakerData () {

return

this . $store . state . speakerData ; },

filteredData () {

const x

this . selectedFilter , filter

new

RegExp ( this . filteredText , ' i ' )

return

this . speakerData . filter ( el

=> {

if (el[x] !==

undefined ) { return el[x] . match (filter) }

else

return

true ; }) } }

Slide 67

Slide 67

< table

class

" scroll "

< thead

< tr

< th

v-for

" key in columns "

      {{ key }} 
    

</ th

</ tr

</ thead

< tbody

< tr

v-for

" (post , i) in filteredData "

< td

v-for

" entry in columns "

< a

:href

" post . Link "

target

" _blank "

        {{ post[entry] }} 
      

</ a

</ td

</ tr

</ tbody

</ table

Slide 68

Slide 68

Slide 69

Slide 69

this . speakerData . forEach ( function ( index ) {

if (val in endUnit) {

// if we already have this location (stored together as key) let's increment it. 
 // this way of writing it is more performant than functional, 
 if you don’t believe me, let’s arm wrestle.

if (key in endUnit[val]) { endUnit[val][key][ 2 ] += magBase; } else { endUnit[val][key]

[lat , long , magBase]; } } else {

let y

{}; y[key]

[lat , long , magBase]; endUnit[val]

y; } })

Slide 70

Slide 70

THREE.JS Single element < div ref= "container"

</ div

Call on mounted mounted() {

//we have to load the texture when it's mounted and pass it in

let earthmap = THREE.ImageUtils.loadTexture( '/world7.jpg' );

this .initGlobe(earthmap); }

Slide 71

Slide 71

//from const geometry = new THREE.SphereGeometry( 200 , 40 , 30 ); //to const geometry = new THREE.IcosahedronGeometry( 200 , 0 );

Slide 72

Slide 72

VUE.JS + SERVERLESS

Slide 73

Slide 73

THANK YOU! @sarah_edo These slides:
http://bit.ly/2DZfxsw