Understanding Cloud Functions

Google Cloud Functions is a lightweight compute solution for developers to create single-purpose, stand-alone functions that respond to Cloud events without the need to manage a server or runtime environment.

Welcome back to another episode of Cloud Functions for Firebase. In the last episode, Doug showed you how global memory and temp disk space was retained between function invocations on the same server instance. In this episode, you will learn how to safely code for your functions that will run in parallel on multiple instances. Check out the resource below to find out more information, and subscribe to the Firebase channel for more app development tutorials!

Cloud Functions Execution Environment docs → http://bit.ly/2XghRWu

Retry asynchronous functions → http://bit.ly/2tGtZCh

Modifying function configuration → http://bit.ly/2H5L2GS

Python runtime → http://bit.ly/2TEW1g4

Google Cloud regions → http://bit.ly/2Hmp5m0

Learn More

Angular (Full App) with Angular Material, Angularfire & NgRx

Firebase Firestore for iOS

Firebase as simple database to React app

Firebase as simple database to React app

Push Notification using Ionic 4 and Firebase Cloud Messaging

Build a CRUD App with Angular and Firebase

Push Notifications in PWA (React.js) Using Firebase

How to Build a Slack App with Node.js and Firebase Cloud Functions?

How to Build a Slack App with Node.js and Firebase Cloud Functions?

In this Firebase Cloud Functions tutorial, you'll learn how to build a Slack App 💬 with Node.js & Firebase Cloud Functions. A guide to serverless Slack Apps with Firebase Cloud Functions & Firestore. CyberJeff is a simple slack bot that helps automate tasks and improve productivity. Slack Apps, or Bots, allow you to extend slack with interactive features that can improve your teams productivity. The following lesson is a step-by-step guide to building a Slack App using Firebase Cloud Functions as the backend server.

Slack Apps, or Bots, allow you to extend slack with interactive features that can improve your teams productivity. The following lesson is a step-by-step guide to building a Slack App using Firebase Cloud Functions as the backend server.

Our Slack App will perform the following tasks.

  • Listen to events, such as a new user joining the #general channel.
  • Retrieve the user’s slack profile.
  • Send a private personalized message.
  • Add a slash command for user-directed actions.
Create a Slack App

At this point, it is assumed you have admin access to a Slack workspace. If not, feel free to create one to follow this tutorial.

Once you have a workspace, create a new Slack App.


Create a Slack App

Verify Cloud Function Ownership

Before Slack can send events to our Cloud Function, it needs to verify that we own the server. Slack performs a handshake by sending an HTTP request with a challenge parameter to the Cloud Function, then the function must respond back with the same value.

Initialize Cloud Functions

Initialize Cloud Functions. This demo uses the TypeScript flavor.

firebase init functions

Build The Challenge Function

The function only needs to respond with the challenge one time.

export const myBot = functions.https.onRequest( (req, res) => {

  // Request from Slack
  const { challenge }  = req.body;

  // Response from You
  res.send({ challenge })

});

Deploy it

firebase deploy --only functions:myBot

Copy the Function URL from the terminal output

Enter The Deployed URL

Subscribe to an event that you want to listen to, then paste in the Function URL from the last step. Slack should automatically verify the URL and give it a green checkmark ✅.


Subscribe to the member_joined_channel event. Notice how our URL is now verified.

Build the Bot

Now it’s time to do some real work.

Install Dependencies

The Slack Node SDK is a monorepo that contains several packages. The Web API can read and modify data in the workspace. Google PubSub will be used to handle long running background tasks in te following steps.

npm install @slack/web-api
npm install @google-cloud/pubsub

OAuth Token

The OAuth token is used to authenticate your bot/server into a workspace so it can interact with your channel (like post messages).


Install the app into your workspace.

Once installed, it will take you directly to the OAuth token. It usually starts with xoxb or xoxp.


Copy the OAuth Token

Copy the OAuth Token and save it as a Firebase Functions environment variable.

firebase functions:config:set slack.token=YOUR-TOKEN

Signing Secrets

When receiving events from Slack, you should validate the signing secret, which can be found in Basic Info panel. This ensures that only requests from Slack can interact with your function by decoding the digital signature of the request.

firebase functions:config:set slack.signing_secret=YOUR-TOKEN

Add Scopes (Permissions)

OAuth scopes define what your app is allowed to do. You can fine tune permissions under the OAuth & Permissions page. Follow the principle of least privledge and only allow your bot access to resources that it actually needs to do its job. In our case, we need to read a user profile and add them to a specific slack channel.

Listen to Events

Our first goal is to listen to events that happen in the Slack workspace. Slack should notify our server anytime a user joins a channel with the member_joined_channel event.

⚡ Your server must respond be quickly, within 3000ms or less, otherwise Slack will timeout and attempt to retry.

So then, how do we build an app that performs a long-running backend process? There are many right answers, but in Firebase, the best option is to enqueue a PubSub Cloud Function. It allows the initial HTTP endpoint to simply hand off the message and respond quickly to Slack.

Import the dependencies and initlizize them with the environment credentials.

import * as functions from 'firebase-functions';

import { WebClient } from '@slack/web-api';
const bot = new WebClient(functions.config().slack.token);

const { PubSub } = require('@google-cloud/pubsub');
const pubsubClient = new PubSub();

HTTP Gateway

The HTTP gateway validates the request, enqueues a PubSub message with the request, then responds with a 200 code to keep Slack happy.

Extra Snippet: verifySlackSignature

export const myBot = functions.https.onRequest( async (req, res) => {

    // Validate Signature
    verifySlackSignature(req); // See snippet above for implementation

    const data = JSON.stringify(req.body);
    const dataBuffer = Buffer.from(data);

    await pubsubClient
            .topic('slack-channel-join')
            .publisher()
            .publish(dataBuffer);

    res.sendStatus(200);

});

PubSub Function

All the heavy-lifting happens in the PubSub function because we have no time-constraints here. The message.json contains the same data you would have handled in req.body in the HTTP function.

  export const slackChannelJoin = functions.pubsub.topic('slack-channel-join')
    .onPublish(async (message, context) => {

    const { event } = message.json; 

    const { user, channel } = event;

    // TODO something cool...

});

Respond To the User as the Bot

In this section, our app makes requests to the Slack API that (1) fetch the user’s Slack profile (2) invite them to a channel, and (3) greet them with a direct message.

Note: The Slack API does not provide typings for the response object, so you’ll have to treat it as any in typescript.

export const slackChannelJoin = functions.pubsub
  .topic('slack-channel-join')
  .onPublish(async (message, context) => {

    const { event } = message.json; 

    const { user, channel } = event;

    // IDs for the channels you plan on working with
    const generalChannel = 'C12345';
    const newChannel = '#froopy-land';

    // Throw error if not on the general channel
    if (channel !== generalChannel) {
        throw Error()
    }

  // Get the full Slack profile

    const userResult = await bot.users.profile.get({ user });
    const { email, display_name } = userResult.profile as any;

    // Invite the slack user to a new channel
    const invite = await bot.channels.invite({
        channel: newChannel,
        user
    });

    // Send a Message
    const chatMessage = await bot.chat.postMessage({
        channel: newChannel,
        text: `Hey ${display_name}! So glad to have you on my Slack!`
    });

});
Additional Ideas to Try

Listen to Slash Commands

Events are great, but somethings you want to give users tools to manually kick off interactivity - that’s where slash commands come in. They work very similar to events, but are are triggered by the user entering /some-command into the workspace.


Slash commands follow the same basic flow as events

Video

How do Cloud Functions work?

How do Cloud Functions work?

How do Cloud Functions work? In this episode of Get to Know Cloud Firestore, Todd explains how to get started with Cloud Functions, shows a working example of one, and then runs you through 5 weird quirks you might encounter when you add Cloud Functions to your app

How do Cloud Functions work? In this episode of Get to Know Cloud Firestore, Todd explains how to get started with Cloud Functions, shows a working example of one, and then runs you through 5 weird quirks you might encounter when you add Cloud Functions to your app

Getting Started with Python working on Google Cloud Functions

Getting Started with Python working on Google Cloud Functions

Writing Python cloud functions is easy and fun, as I will now demonstrate. To follow this tutorial you’ll need a Google Cloud Project, a local Python development environment and the gcloud SDK.

I’ve been a fan of Cloud Functions for a while and a fan of Python since forever, so when support for Python functions was finally announced at Google NEXT’18 I did a small dance of celebration (or at least, I fist-bumped the air when no one was looking). I’m sure you know what Python is, but if you’re new to the serverless world we’re now living in, a quick re-cap on Cloud Functions:

Cloud Functions are small pieces of code that execute in an event-driven manner. They do one small thing very efficiently in reaction to a trigger — usually an HTTP request. The neat thing is you manage zero infrastructure and usually only pay for the execution of your function and a few seconds of compute. You may have heard of other Functions-as-a-Service offerings such as AWS Lambda.

Writing Python cloud functions is easy and fun, as I will now demonstrate. To follow this tutorial you’ll need a Google Cloud Project (you can sign up and get free credits here), a local Python development environment and the gcloud SDK.

Hello, World

We can create a simple function by creating a main.py with just 2 lines:

def hello_world(request):
    return 'Hello, World!\n'

You can deploy this as a cloud Function with the following command. Note that the cloud function name matches the name of the function we defined in code: hello_world

gcloud beta functions deploy hello_world --runtime python37 --trigger-http

After a couple of minutes, the command will return the httpsTrigger or URL for your function. Try accessing this URL and you’ll see the message “Hello, World!” returned. You can also see your function in the Cloud Console UI:

But this is pretty boring stuff, let’s make the function do something a bit more interactive:

def hello_world(request):
    request_json = request.get_json()
    if request_json and 'name' in request_json:
        name = request_json['name']
    else:
        name = 'World'
    return 'Hello, {}!\n'.format(name)

You can use the same gcloud beta functions deploy command as before to update your function. After a couple of minutes, try accessing the URL again and you’ll get the same message. But now try sending it a POST with some data:

curl -X POST <your function URL> -H "Content-Type:application/json" -d '{"name": "Derek"}'

And you should receive a custom message in return! The runtime for Cloud Functions uses the Flask library and provides us with a Request object that we can manipulate however we like.

A More Useful Example

This is all great fun, but now let’s do something useful to show a real world example of where you might employ a Cloud Function. As we’ve seen, Functions are typically triggered by HTTPS endpoints, however you can also make use of Background triggers. Functions deployed this way have no HTTPS endpoint, and are effectively hidden from the Internet, but will instead execute in response to an event such as a file being uploaded to a storage bucket, or a message being sent to a Pub/Sub topic.

Let’s say we have a web service that writes images to a Cloud Storage Bucket. Every time an image is uploaded, we’d like to create a corresponding thumbnail image. Arguably this could be part of our main application code, but for the sake of argument let’s say we want to run this as a separate function, because there may be other ways that images end up in the bucket (maybe an external service sends them to us in batches).

Let’s write a function that reacts when a new file is written to a Cloud Storage bucket. You’ll need 2 GCS buckets for this: You’ll upload images into the first one, and your function will write thumbnails into the second one. In a new directory, create a new main.py file:

from wand.image import Image
from google.cloud import storage

client = storage.Client()

THUMBNAIL_BUCKET = '<your thumbnail bucket>'

def make_thumbnail(data, context):
    # Get the file that has been uploaded to GCS
    bucket = client.get_bucket(data['bucket'])
    blob = bucket.get_blob(data['name'])
    imagedata = blob.download_as_string()
    # Create a new image object and resample it
    newimage = Image(blob=imagedata)
    newimage.sample(200,200)
    # Upload the resampled image to the other bucket
    bucket = client.get_bucket(THUMBNAIL_BUCKET)
    newblob = bucket.blob('thumbnail-' + data['name'])     
    newblob.upload_from_string(newimage.make_blob())

Notice that we’re importing modules, just like we would do with any other Python application. Cloud Functions uses pip, which means we can specify the dependencies we need in a requirements.txt file:

google-cloud-storage
Wand

Just keep your requirements.txt file in the same location as your main.py when you deploy your function.

Don’t forget to replace in the above code with the name of your own thumbnail bucket, and with your source bucket name in the following example. You can now deploy the cloud function like this:

gcloud beta functions deploy make_thumbnail --runtime python37 --trigger-resource <your source bucket>  --trigger-event google.storage.object.finalize

Note: You can’t use the same bucket for both. Why? Because every time the function writes a thumbnail, it would trigger a new invocation of itself!

When you deploy this function you’ll notice we don’t get given a URL. Instead we’ve told the function to run when the google.storage.object.finalize event occurs in the bucket we specified. Let’s upload a picture of Derek the Dog to the source bucket:

Check the thumbnails bucket, and you should see that the function has done its job and created a thumbnail for us!

You can inspect the magic under the hood by revisiting the Cloud Functions part of Cloud Console, where you can track the invocations of your function and even watch its logs in real-time.

Cloud Functions are perfect when you need small pieces of code to tie together larger pieces of a stack. Now that Google has added Python support, we have a huge and diverse ecosystem of functionality available to us to write functions. For more information, read about the Python runtime or follow all of Google’s How To guides.

Thanks for reading!