Skip to content

benjamingreenberg/firebase-email-template-system

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 

Repository files navigation

Firebase email template system

Table of Contents

About

This project uses Google Firebase as a back-end for a basic email template system. It creates HTTP endpoints to generate emails from templates. Requests include the name of the template file to use, and what values to use for replacing the tags in the template. The system has two endpoints for receiving requests: getEmail responds back with the generated email, and sendEmail actually emails it after it is generated.

mustache.js is used for the template syntax, and SendGrid is used to send the emails.

I created this project as a learning exercise. It is not intended to be a robust and production-ready system. The method to prevent unauthorized connections is relatively basic, only one email can be included in each request, and the system has no mechanism to throttle requests.

Also, it's not practical to have the template files reside on the filesystem. A change to a template requires you to redeploy to Firebase. It would be better if the system used Firestore or Google Cloud Storage for the templates.

Getting Started

These instructions will get you a copy of the project up and running on your own system, and deploy it to Firebase.

Prerequisites

Installing

Create Firebase Project

Log into the Firebase Cloud Console, and create a new Firebase Project. There is no need to enable Analytics on the project.

Functions

Go to the Functions page in the Cloud Console, and click the link to upgrade the project to the "Blaze" billing plan; a project must be assigned a Billing Account to use Cloud Functions. You shouldn't exceed the free tier usage during development, but you can set a Budget Alert for $1 just in case.

Firestore Database

  1. Go to the Firestore Database tab in the Firebase Cloud Console and click "Create database".

  2. Make sure "Start in production mode" is selected, and click "Next".

  3. Choose the Cloud Firestore location most suitable for your project, and click "Enable".

Initialize Firebase

Go to the project's directory after downloading or cloning it to your system, and initialize Firebase:

cd my_project
firebase init

Make the following selections:

  • Features: Functions, Emulators
  • Use an existing project, then select your project
  • Language: JavaScript
  • ESLint: No
  • Do NOT overwrite existing files in the /functions directory
    • package.json
    • index.js
    • .gitignore
  • Install dependencies: Yes
  • Emulators: Functions Emulators
  • Port: Choose the default port unless it conflicts with an existing service on your system
  • Emulator UI: Optional

Configure

Copy functions/default_config.js to functions/config.js, and follow the instructions in the file for filling in the values.

Create User

Run firebase functions:shell on the command line and type install({}) to run the install endpoint (type .exit to close the shell afterwards).

$ firebase functions:shell
⚠  functions: The Cloud Firestore emulator is not running, so calls to Firestore will affect production.
i  functions: Loaded functions: install, getEmail, sendEmail
⚠  functions: The following emulators are not running, calls to these services will affect production: firestore, database, pubsub, storage
firebase > install({})
Sent request to function.
firebase > >  {"verifications":{"app":"MISSING","auth":"MISSING"},"logging.googleapis.com/labels":{"firebase-log-type":"callable-request-verification"},"severity":"INFO","message":"Callable request verification passed"}
⚠  Google API requested!
   - URL: "https://oauth2.googleapis.com/token"
   - Be careful, this may be a production service.
>  {"severity":"INFO","message":"No users, creating first..."}

RESPONSE RECEIVED FROM FUNCTION: 200, {
  "result": "Initial account created. Send requests using Client ID L082w9MtPhgd7UXr2aeg and Key 9218cf63-5040-4ea3-b6bb-8d5f98469a2c"
}
########## type .exit to quit the shell ##########
.exit

This is a Callable Function, and can only be called from the app or other authenticated source, like the shell. It cannot be run from your browser or a REST client like getEmail and sendEmail can.

It will create a user in Firestore, if one doesn't already exist, with an ID and Key that will be needed to make requests to the other endpoints. It will fail if a user already exists.

To see existing user(s), along with their ID and Key, browse to the Firestore Console and click on the Firestore Database tab. You can also go there to create users manually.

Deploying

Use the firebase deploy command to create publicly accessible endpoints.

$ firebase deploy

=== Deploying to 'my-project'...

i  deploying functions
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
✔  functions: required API cloudfunctions.googleapis.com is enabled
✔  functions: required API cloudbuild.googleapis.com is enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (120.65 KB) for uploading
✔  functions: functions folder uploaded successfully
i  functions: creating Node.js 14 function getEmail(us-central1)...
i  functions: creating Node.js 14 function sendEmail(us-central1)...
i  functions: creating Node.js 14 function install(us-central1)...
✔  functions[sendEmail(us-central1)]: Successful create operation.
✔  functions[getEmail(us-central1)]: Successful create operation.
✔  functions[install(us-central1)]: Successful create operation.
i  functions: cleaning up build files...

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/my-project/overview

Usage

Send requests to the getEmail or sendEmail https endpoints. You can find the URLs for the deployed endpoints by browsing to the "Functions" tab of the Project Console in Firebase. The endpoints will respond the same to GET or POST requests.

The request header should have content-type set to application/json, and the request body should be a JSON object with a property named emailData containing the data that the system will use to authenticate the request, build the email from a template file, and then return or email it.

getEmail Endpoint

Requests to the getEmail endpoint must include an emailData object, unless useDemoFiles is set to true in config.js (see the Demo/Tutorial section below). In addition to the properties for replacing template tags, emailData must have the following properties:

  • appUserId
  • appUserKey
  • templateFile (the name of a file in the "templates" directory)

sendEmail Endpoint

The sendEmail endpoint has the same requirements as getEmail, but its emailData object must also include the properties related to sending the email:

  • appUserId
  • appUserKey
  • templateFile
  • emailToAddress
  • emailFromAddress
  • emailFromName
  • emailSubject
  • sendGridApiKey (if not set in config.js)

The system assumes that the template contains an HTML version of the email. It will create a plain-text version, and send a multi-part email with both. It will respond with true if the email was sent successfully, or false otherwise.

VSCode Rest Client Extension

Below is an example of a request that you can send in VSCode using the REST Client extension. It contains the same object that is in demo_data.js, and uses templates/demo_template.html as the template file. Replace the values in appUserId and appUserKey with the corresponding values for the user created when installing the project.

POST http://localhost:5000/my-project/us-central1/getEmail HTTP/1.1

content-type: application/json
{
  "emailData": {
    "appUserId": "",
    "appUserKey": "",
    "templateFile": "demo_template.html",
    "sendGridApiKey" : "",
    "emailToAddress" : "foo@example.com",
    "emailFromAddress": "bar@example.com",
    "emailFromName": "Email template system",
    "emailSubject": "Email template system demo",
    "demo_check": "See 'Checking if demo_data.js is as expected' in templates/demo_template.html for information about this property.",
    "second_list_item": "The third list item is {{ third_list_item }}, and will be blank because the 'third_list_item' property has been intentionally left out of demo_data.js",
    "array_of_strings": [
      "First list item",
      "Second list item",
      "Third list item"
    ],
    "array_of_objects": [
      { "id": 1, "name": "Object 1 name" },
      { "id": 2, "name": "Object 2 name" },
      { "id": 3, "name": "Object 3 name" },
      { "id": 4 },
      { "id": 5, "name": "Object 5 name"}
    ]
  }
}

Demo - Tutorial

There is a demo containing a short introduction / tutorial on how to use mustache.js to build templates. It can also be used to quickly verify that everything is configured correctly using your web browser.

To enable the demo, set useDemoFiles in config.js to true. Then copy default_demo_data.js to demo_data.js, and templates/default_demo_template.html to templates/demo_template.html. Open the demo_data.js file, and set appUserId and appUserKey to the corresponding values for the user created when installing the project.

Start the Firebase Functions Emulator and browse to the URL for the getEmail endpoint, which will be in the form http://localhost:PORT/PROJECT_ID/GOOGLE_CLOUD_REGION/getEmail

$ firebase serve

=== Serving from '/path/to/my_project'...

✔  functions: Using node@14 from host.
i  functions: Watching "/path/to/my_project/functions" for Cloud Functions...
⚠  functions: The Cloud Firestore emulator is not running, so calls to Firestore will affect production.
✔  functions[us-central1-install]: http function initialized (http://localhost:5000/my-project/us-central1/install).
✔  functions[us-central1-getEmail]: http function initialized (http://localhost:5000/my-project/us-central1/getEmail).
✔  functions[us-central1-sendEmail]: http function initialized (http://localhost:5000/my-project/us-central1/sendEmail).

To test the sendEmail endpoint, edit demo_data.js and set values for emailToAddress, emailFromAddress, and SendGridAPIKey (unless it is already set in config.js). Browse to the URL for the sendEmail endpoint, which is given when you run firebase serve (see above), and the system should email the demo to the email you specified in the demo_data.js file.

Using Demo files to develop templates

You can use the demo files to develop templates on your local system. Edit demo_data.js and change the values in emailData with values for the template you are working on. Set useDemoFiles in config.js to true, start the Firebase Functions Emulator and browse to the URL of an endpoint. You can then make changes to your template or emailData, and refresh your browser to see the results.

About

System to create and send emails using templates, with Firebase as the back-end.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published