Using Firebase and Email to Send a Custom Diploma to Our Top Performing Users

Torkel Velure
Firebase Developers
5 min readJan 3, 2022

Fjelltoppjakten(the peak hunt) is an app where you collect peaks. The app includes a map of peaks in Norway, and when you are on the top of a mountain you can check-in, collect points and take a picture.

In less than 10 months, some users have already visited more than 500 peaks!

With the year coming to an end I wanted to send a diploma or “certificate of completion” to all the users that had visited all the peaks in Bergen or Osterøy municipality. The diploma should contain the number of mountains they visited, and which municipalities they have completed(visited all peaks).

We have all the user data in Firestore, use Firebase Authentication and have a bunch of Cloud Functions to massage the data.

TL;DR:

  1. Create a plain HTML/CSS website that looks like a diploma
  2. Create an API to modify the text of the diploma
  3. Find all users who should receive the diploma
  4. Fetch their data (mail, name, visited mountains)
  5. Generate the diplomas as PDFs from the website with html-pdf-node
  6. Save the PDFs to Cloud Storage
  7. Send the diplomas using nodemailer

Creating the diploma

I wanted a simple A4 landscape diploma with a “ribbon” and some text, and decided to do it using HTML/CSS.

I won’t focus on this part as it’s quite simple HTML/CSS, but a few things to note:

Creating an A4 landscape sized page with:

A4 landscape page

Creating fancy corners:

CSS for the bottom corner

Creating signatures:
We wrote our signatures on paper and used the Mac camera app to create a transparent image of it.

Creating an API to generate dynamic diplomas

I have had hosting at one.com that supports PHP for years, so I figured that would be the simplest solution. I simply replaced some texts in the diploma with PHP to echo out some GET parameters, and then uploaded the html and css to the web server at /diploma.php

With this I got a website that looks like a diploma, and the text is customizable with GET parameters. e.g https://example.com/diploma.php?name=Torkel Velure&achievement=Visited 252 peaks during 2021, and finished:&achievement2=Bergen,Askoy

Creating a PDF from the website

All browsers support CTRL+P to print a web page, and you can customize it to remove margins and header/footer. Naturally there is a Node.js library that can do the same thing, called html-pdf-node .

Generating a PDF

Uploading the PDF to Cloud Storage

After creating the PDF I uploaded it to Cloud Storage, and generated a signed URL so I can retrieve the PDF later. I think signed URLs expire in 7 days now(?), but that doesn’t matter in this case, as we’ll use the URL immediately after generating it.

Adding the correct text to the diploma based on a user ID

In the Firestore DB of Fjelltoppjakten we have collections like:

users/$userId {name: String, uniqueMountainsVisited: Number, ...}mountains/$mountainId { county: String, municipality: String, ...}userStatistics/$userId/uniqueMountainsVisited/$county {
Bergen: Array<mountainId: String>
Osterøy: Array<mountainId: String>
...municipalityNames
}
userStatistics/$userId/uniqueMountainsVisitedYearly/$year {
mountains: Array<mountainId: String>
}
totals/$county {
Bergen: Number // number of mountains in Bergen,
Osterøy: Number,
...municipalityNames
}

Whenever a user checks in on a mountain a Cloud Function runs that populates the statistics collections/fields.

Whenever a mountain is added, the municipality in the totals collection is incremented.

We want to figure out:

  1. How many mountains a user has visited.
    * Get it from the user document
  2. User name
    * Get it from the user document
  3. Municipalities where the user has visited all the existing peaks/mountains.
    * Find every array in userStatistics/$userId/uniqueMountainsVisited and take those arrays that contain as many mountainIds as the total number found for that municipality in the totals collection..

Generating a diploma with a Cloud Function

Now that we have the building blocks, we can put it together in a HTTP cloud function that returns a URL pointing to the generated PDF.

Now we can simply call https://us-central1-<project-id>.cloudfunctions.net/generatePdf?userId=$userid to generate a diploma for a given user and get a url pointing to the PDF in return.

Be careful though: this function does not require authentication.. You should probably secure it better than I did.

Take note of the timeoutSeconds: 60 and memory: “2GB”. I experienced some problems with memory and slowness, so I increased both values. Generating a PDF and holding it in memory might require a lot of memory, especially with PDFs that contain images.

Sending the diploma to users through mail

Now that we have the ability to generate diplomas, save them to storage and retrieve a URL to download them, we can send them to our users.

Instead of doing it through a Cloud Function, I wrote a script locally instead.

Using the Firebase Admin SDK locally requires some setup:

  1. npm init
  2. npm install dotenv firebase-admin
  3. Generate a Firebase service account key and place it in .keys folder.
    (Firebase Console > gear icon > Project settings > Service accounts > Generate a new private key button.)
  4. Write the path of the json key file in to .env like this: GOOGLE_APPLICATION_CREDENTIALS=".keys/MYPROJECT-firebase-adminsdk-asdf-234lkjjfsoi.json". We will use dotenv to load it later.

With the setup done we can proceed with our use case.

  1. Figure out which users have visited all the peaks in Bergen or Osterøy this year.
  2. Find the users email-addresses through Firebase Authentication.
  3. Find the name of the users.
  4. Create a diploma for each user.
  5. Send diploma through email to those users

Execute node -r dotenv/config index.js and all the diplomas are generated and an email is sent to all the users!

The email we sent (it was originally in Norwegian)

Conclusion

That’s a lot of code and very specific to my use case (and perhaps terrible data architecture), but I’m sure I will be using some of these snippets in future projects, and I hope some of it can help you as well!

--

--