Search Lessons, Code Snippets, and Videos
search by algolia
X
#native_cta# #native_desc# Sponsored by #native_company#

Angular4 Transactional Email With Cloud Functions and Sendgrid

Episode 10 written by Jeff Delaney
full courses and content on fireship.io

Health Check: This lesson was last reviewed on and tested with these packages:

  • Angular v4
  • AngularFire2 v4
  • Sendgrid v2
  • Cloud Functions v0.x

Update Notes: This lesson has been updated for Angular 6, Firestore, and the new SendGrid v3 NodeJS packages. Get the latest Sendgrid v3 Angular Lesson

Find an issue? Let's fix it

In this lesson, I am going to show you how send transactional email in Angular using Google Cloud Functions for Firebase and Sendgrid. You can use any email provider you want, even Gmail, but I will be working with Sendgrid because they have a solid NodeJS example in their documentation.


sendgrid angular cloud functions

GCP Cloud Functions is a serverless architecture that allows you to run isolated code in a NodeJS runtime - as opposed to deploying your own server. It is Google’s answer to AWS Lambda, aka Functions as a Service (FaaS), and has only been available to the public since 2017. It is awesome for Firebase developers because it allows you to run code in the background that would otherwise bog down an Angular app. Sending email on the client side is CPU intensive and should almost always be delegated to a background job.

We can trigger cloud functions in a variety of ways, but this lesson will demonstrate the use of functions via HTTP. When the function is deployed, we will have a URL endpoint that we can use to send query params. It’s identical to working with any other RESTful API. It is also possible to trigger functions on database writes and authentication events, which I plan on covering in future posts.

Initializing Cloud Functions

First, I am assuming you have an Angular app started with the firebase-tools installed. From there, you can run:

firebase init

This will create a functions directory and give us an index.js file, which is were all cloud functions will be defined. The newly created functions directory has its own isolated NodeJS environment, so you make sure are inside the functions directory when installing new packages.

cd functions
npm install sendgrid --save

HTTP Cloud Function

Before going any further, make sure you get your SendGrid API credentials. It is a paid service, but they offer a free tier up to a certain email volume.

index.js

var functions = require('firebase-functions');

const sendgrid = require('sendgrid')
const client = sendgrid("YOUR_SG_API_KEY")

function parseBody(body) {
var helper = sendgrid.mail;
var fromEmail = new helper.Email(body.from);
var toEmail = new helper.Email(body.to);
var subject = body.subject;
var content = new helper.Content('text/html', body.content);
var mail = new helper.Mail(fromEmail, subject, toEmail, content);
return mail.toJSON();
}


exports.httpEmail = functions.https.onRequest((req, res) => {
return Promise.resolve()
.then(() => {
if (req.method !== 'POST') {
const error = new Error('Only POST requests are accepted');
error.code = 405;
throw error;
}


const request = client.emptyRequest({
method: 'POST',
path: '/v3/mail/send',
body: parseBody(req.body)
});

return client.API(request)


})
.then((response) => {
if (response.body) {
res.send(response.body);
} else {
res.end();
}
})

.catch((err) => {
console.error(err);
return Promise.reject(err);
});


})

First, I defined the parseBody helper to convert the email params into JSON from SendGrid’s mail helper. This is a pretty powerful feature of SendGrid when it comes to formatting emails, so check out all the mail helper options here.

The code exports.httpEmail defines the name of the cloud function, which will return a promise when triggered via HTTP. The request must be of type POST and should have query params that define the email addresses, subject, and content. These params will then be sent to the sendgrid.API, which will return a response when the email has been sent.

Deploying the function

Before we can wire up the cloud function with Angular, we need to deploy it.

firebase deploy --only functions

The console will give you a the URL to trigger the function via HTTP. Make a note of this url because you will need it in the next step.

Trigger from a Component

In Angular, we can call this function from any component or service in the app. Let’s build a component that triggers the function with the HTTP module. This code can also be used in a service, then injected into any components that use it.

cd ..
ng g component send-email

Import the HTTP module and create a function that sends a POST request to the URL of the deployed function. I am going to hard code some query parameters just to show how to send data to function.

import { Component } from '@angular/core';
import { Http, Headers, Response, URLSearchParams } from '@angular/http';
import 'rxjs/add/operator/toPromise';

@Component({
selector: 'send-email',
templateUrl: './send-email.component.html',
styleUrls: ['./send-email.component.scss']
})
export class SendEmailComponent implements OnInit {

constructor(private http: Http) { }

sendEmail() {

let url = `https://your-cloud-function-url/function`
let params: URLSearchParams = new URLSearchParams();
let headers = new Headers({'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });

params.set('to', '[email protected]');
params.set('from', '[email protected]');
params.set('subject', 'test-email');
params.set('content', 'Hello World');

return this.http.post(url, params, headers)
.toPromise()
.then( res => {
console.log(res)
})
.catch(err => {
console.log(err)
})

}

}

In the template, you can simply bind the sendEmail function to a click event.

<button (click)="sendEmail()">Send Email via Cloud Function</button>

If all went according to plan, you should see an email in your inbox. If not, make sure to check the console for errors and check the Firebase cloud functions logs. Good luck!

firebase cloud functions logs check the firebase cloud functions logs to catch errors