Firebase Functions in a nutshell

By Mohsen Mahabadi

7 min read

Firebase Functions is a powerful tool that allows developers to build and run backend code for their applications.

Authors

Before we begin, it should be noted that if you are not familiar with Firebase Functions, this article will get you right up to speed! If you are experienced with them, there might be some interesting pieces that you are unaware of.

Let's look at the Firebase Functions definition provided by Google:

Cloud Functions for Firebase is a serverless framework that lets you automatically run backend code in response to events triggered by Firebase features and HTTPS requests. Your JavaScript or TypeScript code is stored in Google's cloud and runs in a managed environment. There's no need to manage and scale your own servers.

Why do we need Firebase Functions

We no longer need to create, distribute, and maintain backend APIs thanks to Firebase Functions. For instance, when a new user signs up with Firebase Authentication and we want to store a record in our database, we also don't want to allow write access to our database from the front end, so we write and deploy a cloud function to handle that task instead. More use cases can be found here if you're interested.

The image shows a simple flow of using firebase functions.

Setup your project

Make sure Node.js and firebase-tools are set up on your computer before moving on because Firebase Functions run in a Node.js environment.

Initialize your project

Open the terminal and navigate to the folder where you want to create the project.

  1. Run firebase login
  2. Run firebase init firestore
  3. Run firebase init functions

After these commands complete successfully, your project structure looks like this:

myproject
+- .firebaserc    # Hidden file that helps you quickly switch between
|                 # projects with `firebase use`
|
+- firebase.json  # Describes properties for your project
|
+- functions/     # Directory containing all your functions code
     |
     +- .eslintrc.json  # Optional file containing rules for JavaScript linting.
     |
     +- package.json  # npm package file describing your Cloud Functions code
     |
     +- index.js      # main source file for your Cloud Functions code
     |
     +- node_modules/ # directory where your dependencies (declared in
                      # package.json) are installed
  1. After completing the setup, you can open the source directory in your favorite IDE and copy the following code on the index.js file.
// The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers
import * as functions from 'firebase-functions'
// the Firebase Admin SDK to access Firestore.
import * as admin from 'firebase-admin'

admin.initializeApp()

Trigger Firebase Functions

There are two methods for calling Firebase Functions. The functions can be called directly by the HTTP Triggers method or as part of an event called Background Triggers.

The image shows methods for calling firebase functions

We can trigger some functions when a user signs up, or trigger a function by adding, updating, or deleting a record from the database. It can be run when the database event occurs.

HTTP Triggers

There are two ways to invoke a cloud function through the HTTP protocol: Endpoint requests and Callable functions.

Endpoint request

Endpoint requests allow you to trigger a function by making an HTTP request to a specific URL. This is useful for integrating with other services, or for triggering a function from a web or mobile app.

1. Request functions

Add the following code to the index file we previously created:

exports.helloWorld = functions.https.onRequest((request, response) => {
 response.send(“Hello World!);
});

To publish the function to Firebase, use the following command:

Firebase deploy --only functions

Then, in Firebase's functions section, you can see the function.

The image displays Firebase Functions' section on Firebase console

After you deploy an HTTP function, you can invoke it using its own unique URL. The URL can be found by going to the functions section.

The image shows where you can find function's URL

The URL includes the following, in order:

  • The region (or regions) to which you deployed your function. Some production functions may need to explicitly set the location to minimize network latency.
  • Your Firebase project ID
  • cloudfunctions.net
  • The name of your function

For example, the URL to invoke helloWorld() looks like this:

https://us-central1-<project-id>.cloudfunctions.net/helloWorld

2. Redirecting
exports.toGoogle = functions.https.onRequest((req, res) => {
  return res.redirect('https://www.google.com')
})

You will be redirected to google.com whenever you access this URL, such as www.mydomain.com/toGoogle.

Callable Functions

Callable functions provide a simple, secure way to invoke a function from a client app. They handle authentication and data serialization and allow you to return a response to the client directly, without the need for additional client-side code.

exports.greeting = functions.https.onCall((data, context) => {
  const { name } = data
  return `Hello, ${name}`
})

Assume that a button (called callBtn) exists and that we want to call our function by clicking on it. Our greeting function will be called each time you click the button.

const button = document.getElementById('callBtn')

button.addEventListener('click', (e) => {
  const greeting = firebase.functions().httpsCallable('greeting')
  greeting({ name: 'Rodney' }).then((res: { data: string }) => {
    console.log(res.data)
  })
})
Checking user authentication:

If in callable function we need to check whether the user is logged in or not, we can use following code:

exports.doSomething = functions.https.onCall((data, context) => {
  if (!context.auth) {
    //user hasn't logged in
    throw new functions.https.HttpsError('unauthenticated', 'you must login.')
  }
  //do something
})

Background Triggers

Background triggers refer to events that trigger a Firebase Function without an explicit request from a client. These triggers can be triggered by events such as changes to a Firebase Realtime Database, or the creation of a new user, and so on.

Auth Events

These triggers allow you to respond to events related to user authentication, such as the creation of a new user account, the deletion of an existing user account, or a user signing in or signing out.

1. User onCreate:

This function will be triggered after adding a user. Note that you must return a value or promise for background triggers.

exports.sendWelcomeEmail = functions.auth.user().onCreate((user) => {
  // ...
})
2. User onDelete:

This function is triggered when a user is deleted.

exports.deleteUser = functions.auth.user().onDelete((user) => {
  console.log('user deleted', user.email)
  return admin.firestore().collection('users').doc(user.uid).delete()
})
3. onAuthStateChanged:

When the user's state changes, this observer is triggered. For instance, the onAuthStateChanged observer is called when the user logs in.

//Auth listener
firebase.auth().onAuthStateChanged((user) => {
  if (user) {
    // user logged in
    // do some tasks like closing modal and so
  } else {
    // user didn't log in
  }
})

Storge Events

With regard to specific Cloud Firestore events, you can create callback functions for

  • onCreate: triggered when a document is written for the first time.
  • onUpdate: triggered when a document already exists and has any value changed.
  • onDelete: triggered when a document with data is deleted.
  • onWrite: triggered when onCreate, onUpdate, or onDelete is triggered.

For instance, we'd like to know when the user collection was last updated:

exports.updateLastUpdated = functions.firestore
  .document(`documents/{id}`)
  .onUpdate((snap, context) => {
    const previousData = snap.before.data()
    const newData = snap.after.data()
    const docId = context.params.id

    const shouldUpdateLastUpdated = previousData.lastUpdated === newData.lastUpdated

    if (shouldUpdateLastUpdated) {
      return admin.firestore().collection('documents').doc(docId).update({
        lastUpdated: Date.now(),
      })
    }

    return null
  })

Firestore onSnapshot

This method allows you to listen to a document. A document snapshot will be created after calling the callback function. Then, another call updates the document snapshot each time the contents change. You can use this function, for example, to display an exchange list and the changes over time without refreshing the page.

firebase.firestore().collection("exchanges")
 .onSnapshot(snapshot => {
     const list = snapshot.map(item => {...item.data, id: item.id});
     console.log(list);
 });

Database Events

When events occur in your Firebase Realtime Database, you can use Firebase Functions to trigger other functions. As an example, you can use a function to automatically send a push notification to all users whenever a new message is added to a specific chat room in your database. These events include when data is written, updated, or deleted:

  • onWrite: which triggers when data is created, updated, or deleted in Realtime Database.
  • onCreate: which triggers when new data is created in Realtime Database.
  • onUpdate: which triggers when data is updated in Realtime Database.
  • onDelete: which triggers when data is deleted from Realtime Database.
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
admin.initializeApp()

exports.sendPushNotification = functions.database
  .ref('/chat_rooms/{chatRoomId}/messages/{messageId}')
  .onCreate((snapshot, context) => {
    const message = snapshot.val()
    const chatRoomId = context.params.chatRoomId
    const messageId = context.params.messageId

    const payload = {
      notification: {
        title: `New message in chat room ${chatRoomId}`,
        body: message.text,
        badge: '1',
        sound: 'default',
      },
    }
    return admin.messaging().sendToTopic(chatRoomId, payload)
  })

This function listens to the path /chat_rooms/{chatRoomId}/messages/{messageId} and when it detects a new message, it gets the message value from the snapshot snapshot.val(), and the chatRoomId and messageId from the context. Then it creates a notification payload with the message title, body and badge, sound and sends the payload to the topic chatRoomId using the admin.messaging().sendToTopic() method.

Please be aware that you need to set up the Firebase Cloud Messaging service to be able to send push notifications.

Analytics Events

For mobile and web apps, Firebase Analytics is a free, unlimited analytics tool that offers information on user engagement and app usage. Firebase Functions can be triggered in response to events in Firebase Analytics, allowing you to automate tasks based on user behavior. For instance, you can use a function to send a push notification to users who haven't opened your app in a certain amount of time.

Here's an example of a Firebase Function that triggers when a user logs an event in Firebase Analytics, and logs the event name and parameters to the console:

/**
 * After a user has completed a purchase, send them a coupon via FCM valid on their next purchase.
 */
exports.sendCouponOnPurchase = functions.analytics.event('in_app_purchase').onLog((event) => {
  const user = event.user
  const uid = user.userId // The user ID set via the setUserId API.
  const purchaseValue = event.valueInUSD // Amount of the purchase in USD.
  const userLanguage = user.deviceInfo.userDefaultLanguage // The user language in language-country format.

  // For purchases above 500 USD, we send a coupon of higher value.
  if (purchaseValue > 500) {
    return sendHighValueCouponViaFCM(uid, userLanguage)
  }
  return sendCouponViaFCM(uid, userLanguage)
})

You might want to access user attributes like the user's language and the event's value (valueInUSD) for a purchase-triggered function, as shown in this sample. This second attribute allows the sample function to test whether this is a high-value conversion event, in order to send a higher-value coupon to valuable customers.

Conclusion

In conclusion, Firebase Functions is a powerful and flexible tool that allows developers to build and run backend code for their applications. With its integration with other Firebase services, such as Realtime Database and Firebase Analytics, it provides a seamless way to automate tasks and respond to events in real-time. Whether it's sending push notifications, logging new data, or tracking user behavior, Firebase Functions provide a simple and efficient way to add custom logic to your application.

With its serverless architecture, it eliminates the need for managing infrastructure and allows developers to focus on building great apps. Whether you're a seasoned developer or just starting out, Firebase Functions is a valuable tool to add to your toolkit.


Resources


Upcoming events

  • Coven of Wisdom - Herentals - Winter `24 edition

    Worstelen jij en je team met automated testing en performance? Kom naar onze meetup waar ervaren sprekers hun inzichten en ervaringen delen over het bouwen van robuuste en efficiënte applicaties. Schrijf je in voor een avond vol kennis, heerlijk eten en een mix van creativiteit en technologie! 🚀 18:00 – 🚪 Deuren open 18:15 – 🍕 Food & drinks 19:00 – 📢 Talk 1 20:00 – 🍹 Kleine pauze 20:15 – 📢 Talk 2 21:00 – 🙋‍♀️ Drinks 22:00 – 🍻 Tot de volgende keer? Tijdens deze meetup gaan we dieper in op automated testing en performance. Onze sprekers delen heel wat praktische inzichten en ervaringen. Ze vertellen je hoe je effectieve geautomatiseerde tests kunt schrijven en onderhouden, en hoe je de prestaties van je applicatie kunt optimaliseren. Houd onze updates in de gaten voor meer informatie over de sprekers en hun specifieke onderwerpen. Over iO Wij zijn iO: een groeiend team van experts die end-to-end-diensten aanbieden voor communicatie en digitale transformatie. We denken groot en werken lokaal. Aan strategie, creatie, content, marketing en technologie. In nauwe samenwerking met onze klanten om hun merken te versterken, hun digitale systemen te verbeteren en hun toekomstbestendige groei veilig te stellen. We helpen klanten niet alleen hun zakelijke doelen te bereiken. Samen verkennen en benutten we de eindeloze mogelijkheden die markten in constante verandering bieden. De springplank voor die visie is talent. Onze campus is onze broedplaats voor innovatie, die een omgeving creëert die talent de ruimte en stimulans geeft die het nodig heeft om te ontkiemen, te ontwikkelen en te floreren. Want werken aan de infinite opportunities van morgen, dat doen we vandaag.

    | Coven of Wisdom Herentals

    Go to page for Coven of Wisdom - Herentals - Winter `24 edition
  • Mastering Event-Driven Design

    PLEASE RSVP SO THAT WE KNOW HOW MUCH FOOD WE WILL NEED Are you and your team struggling with event-driven microservices? Join us for a meetup with Mehmet Akif Tütüncü, a senior software engineer, who has given multiple great talks so far and Allard Buijze founder of CTO and founder of AxonIQ, who built the fundaments of the Axon Framework. RSVP for an evening of learning, delicious food, and the fusion of creativity and tech! 🚀 18:00 – 🚪 Doors open to the public 18:15 – 🍕 Let’s eat 19:00 – 📢 Getting Your Axe On Event Sourcing with Axon Framework 20:00 – 🍹 Small break 20:15 – 📢 Event-Driven Microservices - Beyond the Fairy Tale 21:00 – 🙋‍♀️ drinks 22:00 – 🍻 See you next time? Details: Getting Your Axe On - Event Sourcing with Axon Framework In this presentation, we will explore the basics of event-driven architecture using Axon Framework. We'll start by explaining key concepts such as Event Sourcing and Command Query Responsibility Segregation (CQRS), and how they can improve the scalability and maintainability of modern applications. You will learn what Axon Framework is, how it simplifies implementing these patterns, and see hands-on examples of setting up a project with Axon Framework and Spring Boot. Whether you are new to these concepts or looking to understand them more, this session will provide practical insights and tools to help you build resilient and efficient applications. Event-Driven Microservices - Beyond the Fairy Tale Our applications need to be faster, better, bigger, smarter, and more enjoyable to meet our demanding end-users needs. In recent years, the way we build, run, and operate our software has changed significantly. We use scalable platforms to deploy and manage our applications. Instead of big monolithic deployment applications, we now deploy small, functionally consistent components as microservices. Problem. Solved. Right? Unfortunately, for most of us, microservices, and especially their event-driven variants, do not deliver on the beautiful, fairy-tale-like promises that surround them.In this session, Allard will share a different take on microservices. We will see that not much has changed in how we build software, which is why so many “microservices projects” fail nowadays. What lessons can we learn from concepts like DDD, CQRS, and Event Sourcing to help manage the complexity of our systems? He will also show how message-driven communication allows us to focus on finding the boundaries of functionally cohesive components, which we can evolve into microservices should the need arise.

    | Coven of Wisdom - Utrecht

    Go to page for Mastering Event-Driven Design
  • The Leadership Meetup

    PLEASE RSVP SO THAT WE KNOW HOW MUCH FOOD WE WILL NEED What distinguishes a software developer from a software team lead? As a team leader, you are responsible for people, their performance, and motivation. Your output is the output of your team. Whether you are a front-end or back-end developer, or any other discipline that wants to grow into the role of a tech lead, RSVP for an evening of learning, delicious food, and the fusion of leadership and tech! 🚀 18:00 – 🚪 Doors open to the public 18:15 – 🍕 Let’s eat 19:00 – 📢 First round of Talks 19:45 – 🍹 Small break 20:00 – 📢 Second round of Talks 20:45 – 🙋‍♀️ drinks 21:00 – 🍻 See you next time? First Round of Talks: Pixel Perfect and Perfectly Insane: About That Time My Brain Just Switched Off Remy Parzinski, Design System Lead at Logius Learn from Remy how you can care for yourself because we all need to. Second Round of Talks: Becoming a LeadDev at your client; How to Fail at Large (or How to Do Slightly Better) Arno Koehler Engineering Manager @ iO What are the things that will help you become a lead engineer? Building Team Culture (Tales of trust and positivity) Michel Blankenstein Engineering Manager @ iO & Head of Technology @ Zorggenoot How do you create a culture at your company or team? RSVP now to secure your spot, and let's explore the fascinating world of design systems together!

    | Coven of Wisdom - Amsterdam

    Go to page for The Leadership Meetup

Share