Run Serverless Cron Jobs With Firebase Cloud Functions
Tags: webdev, firebase, firebasefunctions, googlecloud
Published: 2023-07-05 01:44:29

![](https://cdn-images-1.medium.com/max/1600/1*fLXfXsZi6Mu5UDicyWWuhw.png align="left")

Run a job every 2 minutes with Google Cloud Functions

When a user’s subscription within your app expires, how do the systems you have built know to notify them? If you're building a fintech that is involved in lending, how do you send repayment reminders to users a few days to their repayment and on the repayment day? You may also need to backup your databases at the end of each business day, how do you spin up a service to do that? These and many more are why cron jobs matter. In this tutorial, rather than building out a service that runs cron jobs, we’ll hand all of the work over to a managed service like Google Cloud scheduler.

Google Cloud scheduler combined with Firebase cloud functions allows us to write, deploy and run code that completes a task at any given time. In this tutorial, I’ll show you how we automate loan repayment reminders to our users using firebase functions and the google cloud scheduler.

### Getting Started

Set up a new Firebase project by following the **Getting Started with Firebase** section in my article published [here](https://hackernoon.com/all-the-firebase-functions-you-need-to-build-your-next-serverless-application). Once your firebase project is setup, you’ll need to upgrade from **Spark** which is the free firebase plan to **Blaze** — a paid plan. The scheduler function we’ll be using is only available on the paid plans of firebase. Follow the instructions [here](https://docs.firerun.io/getting-started/upgrading-from-the-firebase-spark-plan-to-the-blaze-plan-tled) to upgrade your plan.

Install firebase admin and firebase tools globally using the commands

```plaintext
npm install firebase-functions@latest firebase-admin@latest — save
npm install -g firebase-tools
```

3\. If you’re on a Mac or a UNIX based system, you may run into a permissions error during installation, prefix your command line commands with `sudo` . With our libraries installed, let’s get into it!

### Setting up your Functions

Next, we need to log in to firebase from our command line to connect it to the project we created in Step 1. Run the command   
\`firebase login\`. This will redirect you to your browser, prompting you to login via the UI. Login via the E-mail you connected to your firebase account to continue. Once in, return to the terminal

Navigate into the directory where you’ll like to create your new functions project. Run the command,

```plaintext
firebase init functions
```

Select the following options in the prompt

```plaintext
-> Use an existing project
-> Select the project you created in Step 1
-> Select JavaScript
-> When prompted to use ESlint, you can select Yes/N. For this example we'll go with N
-> Select Y to install dependencies
```

By completing the steps above, you should now see a functions folder created for you in your directory. Let’s run some tests to check that everything works. We’ll build a quick app that tells us the time in the US, UK, and Nigeria.

### Testing Your Setup using Luxon

Navigate into your functions folder and install the luxon package — a recommended alternative to moment. We’ll be using this package

```plaintext
npm i luxon
```

In functions &gt; index.js, write the following piece of code. I’ve added comments for clarity.

```javascript

const { onRequest } = require("firebase-functions/v2/https");
const logger = require("firebase-functions/logger"); // for logging to the terminal
const { DateTime } = require("luxon"); // get the DateTime class from Luxon

exports.timeInDiffPlaces = onRequest((req, res) => {
  const dt = DateTime.now();

  logger.log(
    "timeInLondon",
    dt.setZone("Europe/London").toLocaleString(DateTime.DATETIME_FULL)
  );
  logger.log(
    "timeInNewYork",
    dt.setZone("America/New_York").toLocaleString(DateTime.DATETIME_FULL)
  );
logger.log(
    "timeInLagos",
    dt.setZone("Africa/Lagos").toLocaleString(DateTime.DATETIME_FULL)
  );
  res.send("success");
});
```

9\. Test out your code by running the command

```plaintext
firebase emulators:start
```

This will create endpoints for each function in your index.js file within the functions folder. The endpoint will be displayed in your terminal window e.g [**http://127.0.0.1:5001/adebola-reminder/us-central1/timeInDiffPlaces**](http://127.0.0.1:5001/adebola-reminder/us-central1/timeInDiffPlaces)

Now, when you go to the endpoint (in your browser) that is displayed in the terminal, you should see a page that simply displays **success** as we have specified in our `res.send`. But take a look in your terminal window, the **logger** library will log out the time in New York, London, and Lagos in your terminal like below.

![](https://cdn-images-1.medium.com/max/1600/1*hyw3ViS85QmltR89ZNB4yg.png align="left")

### Scheduling E-mails to Users

Now that we have everything working fine, let’s get started writing a function to send E-mails to our users. Later, we’ll set up a cloud scheduler to trigger the sending of these emails.

### Setting up Nodemailer and Gmail

We’ll be using **nodemailer** and **Gmail** to send our E-mails. Follow my article [here](https://blog.logrocket.com/send-emails-nodejs-nodemailer/) on setting up both.

Since that article was written, Google has updated how apps can use your E-mail. Google now requires 2FA to be setup to connect it to Nodemailer. Setup 2FA on your E-mail by going [here](https://myaccount.google.com/u/4/security?pageId=none). Once done, we’ll also need to set up an **app password** to login to our gmail account remotely.

Learn more about app passwords [here](https://support.google.com/accounts/answer/185833?visit_id=638238208014451629-1590373095&p=InvalidSecondFactor&rd=1). To setup App password,

1. Go [here](https://myaccount.google.com/u/4/security?pageId=none). Make sure you’re logged in to your Gmail account.
    
2. Under **How you sign in with Google**, select 2-step verification.
    
3. You’ll be prompted to sign in to your E-mail again. Sign in
    
4. Scroll down to App passwords. Click App passwords
    
5. Enter a name for your app. Then, click Generate. A modal will pop up with your app password.
    

The final code is below.

```javascript
const hbs = require("nodemailer-express-handlebars");
const nodemailer = require("nodemailer");
const path = require("path");

// initialize nodemailer
var transporter = nodemailer.createTransport({
  service: "gmail",
  auth: {
    user: "youremail@gmail.com",
    pass: "your_app_password",
  },
});

// // point to the template folder
const handlebarOptions = {
  viewEngine: {
    partialsDir: path.resolve("./views/"),
    defaultLayout: false,
  },
  viewPath: path.resolve("./views/"),
};

// // use a template file with nodemailer
transporter.use("compile", hbs(handlebarOptions));

exports.sendEmailToUsers = onRequest((req, res) => {
  // setup the E-mail options
  const mailOptions = {
    from: '"Happpiness App" <youremail@gmail.com>', // sender address
    to: "your_test_email@gmail.com", // list of receivers
    subject: "Welcome!",
    template: "email", // the name of the template file i.e email.handlebars
    context: {
      name: "Adebola", // replace {{name}} with Adebola
      company: "My Company", // replace {{company}} with My Company
    },
  };

  // trigger the sending of the E-mail
  transporter.sendMail(mailOptions, function (error, info) {
    if (error) {
      return console.log(error);
    }
    logger.log("Message sent: " + info.response);
  });

  res.send("success");
});
```

Again, test that everything works by hitting the URL in your terminal window followed by `/sendEmailToUsers` . Later on, we’ll extract this code into a function of its own rather than having it directly inside the serverless function.

### Now for the much-awaited Scheduler

Now that we can trigger E-mails to users, we can setup a scheduler in Google cloud to trigger our function every so often. For this use case, we want to send reminders to users to repay their loan when 3 days to their repayment dates. I’ll skip the code for figuring out when a user’s repayment falls on and go ahead to show you how to set up your scheduler to call the **sendEmailToUsers** serverless function.

First, extract your E-mail sending logic into a new async function, like below

```javascript
const sendEmailToUsers = async (emails) => {
 // past the code from Setting up Nodemailer and Gmail section above
// remember that E-mails would contain an array of object so adjust your code
// accordingly
}
```

Next, setup the logic you need to get the names, emails and other details of the users you’ll like to E-mail. Remember to adjust the template in your **views** folder from the **setting up nodemailer and gmail** section to reflect your use case.

We’ll also need to import the **onSchedule** function from firebase into our code. Like below;

```javascript
const { onSchedule } = require("firebase-functions/v2/scheduler");
```

Do this at the top of the index.js file where you have your other imports.

Finally, write the scheduler function using the **App Engine syntax** to trigger the **sendEmailForRepayment** function **every 2 minutes** to make sure everything works correctly. The App engine syntax reads like plain English e.g `every 2 minutes` compared to the crontab syntax which looks like   
`*/2 * * * *` representing every 1 minute. You can play around with the crontab syntax [here](https://crontab.guru/). The crontab guru tool will help you generate the appropriate crontab syntax for your scheduler.

You may also use the crontab syntax which most people are familiar with.

```javascript
exports.sendEmailsForRepayment = onSchedule("every 2 minutes", async (event) => {
  // get users whose repayments are due
  await getUsersWithDueRepayments()
  const emailsWithRepaymentDetails = await getRepaymentDetailsForUsers()
  await sendEmailToUsers(emailsWithRepaymentDetails);
  logger.log("Schedule done running");
});
```

### Deploying your Scheduled Cloud functions

Make sure to run your code using the firebase emulator to test that everything works on your local before deploying. Deploy your code using

```plaintext
firebase deploy
```

Your scheduled cloud functions should now be live!

Your schedule function should run every minute as specified in the **onSchedule** callback.

### Logging

To monitor the performance of your serverless functions, head over to your [google cloud console](https://console.cloud.google.com/). Make sure you’re in the right firebase project by selecting your project name from the dropdown.

Use the searchbar to search for the name of your function, in our case `sendEmailsForRepayment` . From here you’ll be able to see all the information on how that function is performing.

### Pub/Sub vs HTTPs

In this example, we connected to our serverless functions via HTTP requests. In my next article, we’ll talk about another way to communicate with serverless functions using Pub/sub or publisher and subscriber. Pub/subs are event-driven.