Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go beyond the web development with Firebase Clo...

Sofiya
November 04, 2017

Go beyond the web development with Firebase Cloud Functions

The main thought - developers should be able to enjoy their work.
Assume you’ve started working on development of the web application. By the time you’ve got to the spot of enjoyable developemt, you would have to go through all the complex details of building the basement for your app, taking care about the backend, servers and more...Cloud Functions allows Firebase developers to create a backend without having to worry about servers. During the talk you will learn what's possible with Cloud Functions and how it can be used for many different key use cases for your web app and not only. You'll see real world examples that show how Cloud Functions for Firebase is the programmatic glue that allows you to extend and connect Firebase products and cloud services.

Sofiya

November 04, 2017
Tweet

Other Decks in Programming

Transcript

  1. Go beyond web development with Firebase Cloud Functions Sofia Huts

    Software Engineer @ JustAnswer Manager @ GDG Lviv
  2. But what if I want to.. • synchronize data from

    another source, outside the database • send an email or push notification based on changes that happened in database • perform heavy operations (e.g. image resizing, video transcoding) • expose HTTP endpoints
  3. • Run in serverless environment • Isolated backend code pieces

    as “microservice” • Autoscales up on load, and down to zero • Node JS code
  4. Triggers HTTP Firebase real-time database / firestore Firebase authentication Firebase

    analytics / crashlytics Cloud pub / sub Cloud storage HTTP
  5. Post GitHub commits to a workgroup chat room in Slack

    https://firebase.google.com/docs/functions/use-cases
  6. myproject +- .firebaserc # hidden file which help you quickly

    switch between | # Firebase projects using ‘firebase use’ | +- firebase.json # describes properties for your project | +- functions/ # directory containing all your functions code | +- package.json # npm package file describing your Cloud Functions | # code and all dependencies | +- index.js # main source file for your Cloud Functions code | +- node_modules/ # directory where your dependencies (declared in # package.json ) are installed
  7. const functions = require('firebase-functions'); exports.lastUpdate = functions.database .ref("/messages/{messageId}").onUpdate(event => {

    const msg = event.data.val(); msg.lastUpdated = new Date().getTime(); return event.data.adminRef.set(msg); })
  8. const functions = require('firebase-functions'); exports.lastUpdate = functions.database .ref("/messages/{messageId}").onUpdate(event => {

    const msg = event.data.val() msg.lastUpdated = new Date().getTime() return event.data.adminRef.set(msg); })
  9. const functions = require('firebase-functions'); exports.lastUpdate = functions.database .ref("/messages/{messageId}").onUpdate(event => {

    const msg = event.data.val() msg.lastUpdated = new Date().getTime() return event.data.adminRef.set(msg); })
  10. const functions = require('firebase-functions'); exports.lastUpdate = functions.database .ref("/messages/{messageId}").onUpdate(event => {

    const msg = event.data.val() msg.lastUpdated = new Date().getTime() return event.data.adminRef.set(msg); })
  11. const functions = require('firebase-functions'); exports.lastUpdate = functions.database .ref("/messages/{messageId}").onUpdate(event => {

    const msg = event.data.val(); msg.lastUpdated = new Date().getTime() return event.data.adminRef.set(msg); })
  12. const functions = require('firebase-functions'); exports.lastUpdate = functions.database .ref("/messages/{messageId}").onUpdate(event => {

    const msg = event.data.val(); msg.lastUpdated = new Date().getTime(); return event.data.adminRef.set(msg); })
  13. Flame Plan $25/month Spark Plan Free Blaze Plan Pay as

    you go 125K/month 40K/month 40K/month Google services only 2M/month 400K/month 200K/month 5 GB/month $0.40/million $0.0025/thousand $0.01/thousand $0.12/GB Invocations GB-seconds CPU-seconds Outbound networking Cloud Functions
  14. Resources Invocation time • 0.7 sec RAM/CPU • 256 MB

    • 400 MHz Invocations • 10K Compute time (256/1024) x 0.7s = 0.175 GB-seconds per invocation (400/1000) x 0.7s = 0.280 GHz-seconds per invocation 10 000 x 0.175 = 1 175 GB-seconds per month 10 000 x 0.280 = 1 280 GHz-seconds per month Networking 0 GB egress traffic Calculations
  15. Billing Metric Gross Value Free Tier Net Value Unit Price

    Total Price Invocations 10 000 2 000 000 -1 990 000,00 $0.0000004 $0,004 GB-seconds 1 175 400 000 -398 825,00 $0.0000025 $0,0029375 GHz-seconds 1 280 200 000 -198 720,00 $0.00001 $0,0128 Networking 0 5 -5,00 $0.12 $0 Total / Month $0,0197375
  16. const functions = require('firebase-functions'); const admin = require('firebase-admin'); admin.initializeApp(functions.config().firebase); const

    scheduleGenerator = require('./schedule-generator-helper.js').generateSchedule; exports.scheduleWrite = functions.database .ref("/schedule").onWrite(event => { const schedulePromise = event.data; const sessionsPromise = admin.database().ref('/sessions').once('value'); const speakersPromise = admin.database().ref('/speakers').once('value'); return generateScheduleOnChange(schedulePromise, sessionsPromise, speakersPromise); });
  17. My plan was • Start free 300$ trial on GCP

    • Use pub/sub, so we can trigger function in GCP • In Firebase function publish topic “db-updated” • In Cloud function subscribe for that topic • In Cloud function use Slack webhook to send messages in channel
  18. const functions = require('firebase-functions'); const admin = require('firebase-admin'); exports.scheduleWrite =

    functions.database .ref("/schedule").onWrite(event => { if (event.auth.admin) { const topic = createTopic('db-changed'); const data = { changedData: event.data._delta, dataPath: '/schedule', database: functions.config().firebase.databaseURL }; publishMessage(topic, data); } }); Only if Admin changes
  19. function createTopic(topicName) { const pubsubClient = pubsub(); const topic =

    pubsubClient.topic(topicName); if (topic) { console.log(`Topic ${topic.name} already exists.`); return topic; } pubsubClient.createTopic(topicName) .then((results) => { const topic = results[0]; console.log(`Topic ${topic.name} created.`); return topic; }); }; const pubsub = require('@google-cloud/pubsub'); Returns topic object if exists Create topic for pub / sub
  20. function publishMessage (topic, data) { // Create a publisher for

    the topic (which can include additional batching configuration) const publisher = topic.publisher(); // Publishes the message as a string, e.g. "Hello, world!" or JSON.stringify(someObject) const dataBuffer = Buffer.from(JSON.stringify(data)); return publisher.publish(dataBuffer) .then((results) => { const messageId = results[0]; console.log(`Message ${messageId} published.`); return messageId; }) .catch((err) => { console.error(`An error occured during publish: ${err}`); }); } Accepts only encoded string Publish to pub / sub
  21. Subscribe in GCP exports.subscribe = function subscribe(event, callback) { //

    The Cloud Pub/Sub Message object. const pubsubMessage = event.data; let data = Buffer.from(pubsubMessage.data, 'base64').toString(); let dataObj = JSON.parse(data); postToSlack(dataObj).then(() => { console.log("Message sent"); }).catch(error => { console.error(error); }); // Don't forget to call the callback. callback(); };
  22. Post to Slack const rp = require('request-promise'); function postToSlack(data) {

    return rp({ method: 'POST', uri: 'https://hooks.slack.com/services/....', body: { text: `:fire: Hello from GCP! New changes arrived in ${data.dataPath}, attachments: [ { title: `Check ${data.database}`, title_link: `${data.database}${data.dataPath}`, text: `${JSON.stringify(data.changedData)}`, color: "#FFC107" } ] }, json: true }); }
  23. const functions = require('firebase-functions'); const admin = require('firebase-admin'); exports.scheduleWrite =

    functions.database .ref("/schedule").onWrite(event => { if (event.auth.admin) { const topic = createTopic('db-changed'); const data = { changedData: event.data._delta, dataPath: '/schedule', database: functions.config().firebase.databaseURL }; publishMessage(topic, data); } });
  24. const functions = require('firebase-functions'); const admin = require('firebase-admin'); exports.scheduleWrite =

    functions.database .ref("/schedule").onWrite(event => { if (event.auth.admin) { const data = { changedData: event.data._delta, dataPath: '/schedule', database: functions.config().firebase.databaseURL }; postToSlack(data).then(() => { console.log("Message sent"); }).catch(error => { console.error(error); }); } });
  25. What if I... • Want to have backups of my

    DB • Don’t wanna enable Blaze plan • My DB is quite small, so I can store data for FREE
  26. const functions = require('firebase-functions'); const fse = require('fs-extra'); exports.saveBackup =

    functions.database.ref('/{node}').onWrite(event => { const maxBackupsCount = 10; const data = event.data.val(); fse.writeJson('/tmp/db-data.json', data) .then(() => { console.log('Successfuly written data to /tmp folder') }) .catch(err => { console.error(err) }); const gcs = require('@google-cloud/storage')(); // Reference to an existing bucket. const bucket = gcs.bucket('<PROJECT_ID>.appspot.com');
  27. //Check files count. If get to limit - delete the

    oldest one bucket.getFiles({ prefix: 'backup/db-data'}).then((result) => { let files = result[0]; if (files.length < maxBackupsCount) { return; } else { // sort by date desc - oldest first const sortedFiles = files.sort(function(a,b) { return new Date(a.metadata.updated) - new Date(b.metadata.updated); }); const fileNameToDelete = sortedFiles[0].name; bucket .file(fileNameToDelete ) .delete() .then(() => { console.log(`${fileNameToDelete} deleted.`); }) .catch(err => { console.error('ERROR:', err); }); } }) .catch(err => console.error(err));
  28. //Upload a local file to a new file to be

    created in bucket. bucket.upload('/tmp/db-data.json', { destination: `/backup/db-data-${new Date().getTime()}.json` }) .then(() => { console.log('Backup was successfuly uploaded!'); }) .catch((err) => { console.error(`Error occured during upload: ${err}`); }); }); //End of saveBackup functions for Firebase
  29. To sum up • Let the Cloud functions help you,

    delegate your work • Try to write optimised function as much as you can (time of execution matters, you pay for it) • Use GSP free tier with 300$ trial, and it will apply to your Firebase project as well • Enjoy this magic!