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

SMS Texting With Twilio and Firebase Cloud Functions

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

Sending SMS text messages to users can push the user experience to the next level. In this lesson, we are going to use the Programmable SMS API from Twilio to notify users when their pizza order has been updated.

Demo of sms texting in angular app with firebase cloud functions.

Initial Setup

We’re going to build a confirmation page for a pizza ordering app. After a customer orders a pizza, they want to stay updated its status. Our feature will send them an SMS text message whenever the status is updated in the Firebase database.

Prerequisites

This lesson assumes you have an Angular app with firebase configured via AngularFire2. You should also have basic knowledge of Firebase Cloud Functions.

Sign up for Twilio

First, you will need to have a Twilio account, which uses pay-by-volume pricing model. You can sign up for a free preview and they will give you a $15 USD credit to play around with the API.

Sign up with twilio for a free api key

Angular Component

Our component is just a hard coded mockup of an order status page. You can think of this as a page where an admin user would update the status of an order.

ng g component pizza-status

Data Structure

I have added an orders collection to the database with some dummy data.

Our pizza data in the database

Add Reactive Forms to NgModule

Make sure you import the Reactive forms module in the NgModule associated with this component. In this example it is the app.module.ts file.

import { PizzaStatusComponent } from './pizza-status/pizza-status.component';
import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
declarations: [
AppComponent,
PizzaStatusComponent // <-- here
],
imports: [
BrowserModule,
ReactiveFormsModule // <-- here
],
bootstrap: [
AppComponent
]
})
export class AppModule { }

pizza-status.component.ts

The Angular component will use a reactive form to collect and validate a phone number for the user. We are required to format this number to an E164 string (looks like +19165551234) before sending it to Twilio. The form validations will be handled with the reactive forms module in Angular.

The order status will be updated on the confirmation page asychronously. At that exact moment, we will also trigger a Firebase Cloud Function that tells Twilio to send the text.

import { Component, OnInit } from '@angular/core';
import { AngularFireDatabase } from 'angularfire2/database';

import { Validators, FormGroup, FormBuilder } from '@angular/forms';


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

numberForm: FormGroup;
order: any;

constructor(private db: AngularFireDatabase, private fb: FormBuilder) { }

ngOnInit() {
this.buildForm()
this.order = this.db.object('/orders/testPizza123')
}

updatePhoneNumber() {
this.order.update({ phoneNumber: this.e164 })
}

buildForm() {
this.numberForm = this.fb.group({
country: this.validateMinMax(1, 2),
area: this.validateMinMax(3, 3),
prefix: this.validateMinMax(3, 3),
line: this.validateMinMax(4, 4)
});
}

/// helper to add validations to form based on min/max length
validateMinMax(min, max) {
return ['', [
Validators.required,
Validators.minLength(min),
Validators.maxLength(max),
Validators.pattern('[0-9]+') // validates input is digit
]]
}

/// converts the current form values to E164
get e164() {
const form = this.numberForm.value
const num = form.country + form.area + form.prefix + form.line
return `+${num}`
}


}

adding the phone number to firebase via angular for text updates

pizza-status.component.html

This demo is using ngClass with CSS classes from the Bulma framework to change the background of the order status.

  <h1>Order Status: 
<span class="tag is-large"
[ngClass]="{
'is-dark' : pizza.status == 'submitted' ,
'is-warning' : pizza.status == 'cooking',
'is-primary' : pizza.status == 'on its way',
'is-success' : pizza.status == 'delivered'
}">

{{ pizza.status }}
</span>
</h1>

<ul>
<li>Order Number: {{ pizza.$key }}</li>
<li>Topping: {{ pizza.topping }}</li>
<li>Price: {{ pizza.price }}</li>
</ul>

<hr>
<h1>Get Updates via Text Message</h1>

<form [formGroup]="numberForm" (ngSubmit)="updatePhoneNumber()" novalidate>
<input type="text" formControlName="country" placeholder="1">
<input type="text" formControlName="area" placeholder="916">
<input type="text" formControlName="prefix" placeholder="555">
<input type="text" formControlName="line" placeholder="5555">

<input type="submit" value="Get SMS Updates" [disabled]="numberForm.invalid">
</form>

<p *ngIf="numberForm.invalid && numberForm.touched">That's not a valid phone number</p>


<h3> Updates will be texed to {{ pizza.phoneNumber || 'none' }}</h3>

Twilio Cloud Function

Now the fun can begin. Our goal is to send the user a text message whenever their order status has been updated. When the pizza goes from cooking to on its way, the user should get a SMS text message from Twilio saying “Your order is on its way with the driver”.

Initialize Firebase Cloud Functions

If you’re not already using Firebase Cloud Functions, you can get started by running:

firebase init functions

Then we will add the Twilio API key to the cloud function environment.

firebase functions:config:set twilio.sid="YOUR_ACCOUNT_SID" twilio.token="YOUR_AUTH_TOKEN"

Make sure you are in the functions directory, then install the twilio helper for NodeJS.

cd functions
npm install twilio --save

Database Cloud Function that Sends SMS Texts

The cloud function uses the onUpdate trigger to run code when the order status changes. Here’s a step by step breakdown of how the function works.

You need to have billing enabled on your Firebase account to send 3rd party API calls.

  1. Initialize Firebase and Twilio with API credentials.
  2. Listen to the /orders/$orderKey/status ref in the database, run function on changes.
  3. Get the full order from the database.
  4. If order has a valid E164 number, make API call to Twilio.
  5. Wait from Twilio’s response and we’re done.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

const twilio = require('twilio');

const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG);
const accountSid = firebaseConfig.twilio.sid;
const authToken = firebaseConfig.twilio.token;

const client = new twilio(accountSid, authToken);

const twilioNumber = '+15555551234' // your twilio phone number


/// start cloud function

exports.textStatus = functions.database
.ref('/orders/{orderKey}/status')
.onUpdate(event => {


const orderKey = event.params.orderKey

return admin.database()
.ref(`/orders/${orderKey}`)
.once('value')
.then(snapshot => snapshot.val())
.then(order => {
const status = order.status
const phoneNumber = order.phoneNumber

if ( !validE164(phoneNumber) ) {
throw new Error('number must be E164 format!')
}

const textMessage = {
body: `Current order status: ${status}`,
to: phoneNumber, // Text to this number
from: twilioNumber // From a valid Twilio number
}

return client.messages.create(textMessage)
})
.then(message => console.log(message.sid, 'success'))
.catch(err => console.log(err))


});


/// Validate E164 format
function validE164(num) {
return /^\+?[1-9]\d{1,14}$/.test(num)
}

Deploy the Function

The final step is to deploy the function.

firebase deploy --only functions

When you update the status property on the order, you should get a text to the supplied phone number each time you update the associated order status. If you run into problems, check the FCF logs or reach out to our Slack team. Good luck!

The End

Twilio combined with Firebase Cloud Functions makes it easy to send text messages to users. Let me know what you want to see next in the comments or via Slack.