An Easy Guide to Firebase Pub/Sub

First step of setting up a pub/sub cloud function in firebase is to ask yourself if this solution is appropriate for your use case. Here's an overview of firebase pubsub usecases:

Scheduled/Cron Triggered: Will be set off at a scheduled time based on a cron expression.

Used when:

  • You need some functionality to occur every X interval. X can be in terms of minutes, hours, days, etc...
  • For a clearer picture of the variety of intervals you can set, visit Crontab Guru's examples page.

Topic Triggered: Will be set off when a message is published to a specific topic.

Used when:

  • You need to perform sequential steps but each step takes some time (firebase functions timeout after a minute by default, you can raise the timeout to 9 minutes but this is not recommended for scaling).
  • You have multiple things that need to be done in parallel when an event occurs. (i.e. multiple subscribers to one topic).
  • You need to react to events quickly without affecting the event publisher.

Examples

// Scheduled/Cron Triggered PubSub
const scheduledFunction = functions.pubsub.schedule('every 5 minutes').onRun((context) => {
    // Do stuff
});

// Event Triggered PubSub
const topicTriggered = functions.pubsub.topic('topic-name').onPublish((message) => {
    // Do stuff
});

I'm a firm believer that showing simple code is the easiest way to learn how to use a new dev tool. Let's see a simple example of both functions in action (in typescript obviously, I'm not an animal).

Here is our user story for this code snippet: I want to tweet out a message every 5 minutes.

import { PubSub } from '@google-cloud/pubsub';
import * as Twit from 'twit';

// Interfaces
interface Tweet {
    title: string;
    message: string;
}

// Topics /
const enum Topics {
    NEW_TWEET = 'new-tweet',
}

// Clients
const pubsub = new PubSub({
    projectId: 'INSERT_PROJECT_ID_HERE',
    credentials: require('../service_account_secret.json'),
});

const twitter = new Twit({
    consumer_key: 'TWITTER_CONSUMER_KEY',
    consumer_secret: 'TWITTER_CONSUMER_SECRET',
    access_token: 'TWITTER_ACCESS_TOKEN',
    access_token_secret: 'TWITTER_ACCESS_TOKEN_SECRET',
});

// Every 5 minutes, tweet out a message
const send_out_new_tweet = functions.pubsub.schedule('every 5 minutes').onRun((context) => {
    const tweet: Tweet = { title: 'Hello', messsage: 'I went through pubsub to get to you' };
    const dataBuffer = Buffer.from(JSON.stringify({ tweet: tweet }));
    await pubsub.topic(Topics.NEW_TWEET).publish(dataBuffer);
});

// Publish a new tweet
const new_tweet = functions.pubsub.topic(Topics.NEW_TWEET).onPublish((message) => {
    const tweet = message.json as Tweet;
    const update = tweet.title + '\n' + tweet.message;
    await twitter.post('statuses/update', { status: update });
});

Analysis

You may be thinking that i could have easily added the tweeting logic inside sendoutnew_tweet so why did i add the extra step of publishing a message to the queue followed by the message being consumed & read by the new_tweet consumer.

What we've effectively done in the above code block is created a neat/scalable system for tweeting out new messages.

  • If i wanted to add another function to tweet out something else, i will only have to worry about WHAT to tweet not HOW to tweet.
  • If one day i got a bunch of errors and nothing was tweeted, i would only have to look at new_tweet.
  • If a bunch of tweets were sent out but only 1 type was not, i would look at the publisher functions.

Firebase Pub/Sub functions can be used in a variety of use cases and can lend some real flexibility to your infrastructure.