SMS and MMS Notifications with Node.js and Express

Today we're going to get your server to automatically sound the (textual) alarm when something goes wrong.  Using Node.js and the Express framework, we'll light up the phones of all of your server administrators when there's an exception.

Clone the sample application from Github, here.  Then head to the application's README.md to see how to run it locally.

See how EMC uses Twilio SMS to send IT alerts to 68,000 employees.

Let's get started!

Configure a Twilio REST client

In order to send messages we'll need to create a Twilio REST client, which requires first reading a TWILIO_ACCOUNT_SID, TWILIO_NUMBER, and TWILIO_AUTH_TOKEN from environmental variables.

We also make the variables available through the config module.

The values for your account SID and Auth Token come from the Twilio console:

Twilio Account Summary section of the console

Click the eyeball icon to expose your Auth Token to copy/paste.

You will have to use a purchased number for the TWILIO_NUMBER variable.  Phone numbers will be in the hash ('#') phone number pane.

The link above will explain how to set environmental variables on Mac OSX, Windows, and *NIX (although may vary based on choice of shell).  On other platforms it might be in a console or set in some other way.  You probably have to read platform specific documentation to see where to set these.  Read it, though - best practice dictates you keep them out of the code itself.

Loading Code Samples...
Language
var dotenv = require('dotenv');
var cfg = {};

if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
  dotenv.config({path: '.env'});
} else {
  dotenv.config({path: '.env.example', silent: true});
}

// HTTP Port to run our web application
cfg.port = process.env.PORT || 3000;

// A random string that will help generate secure one-time passwords and
// HTTP sessions
cfg.secret = process.env.APP_SECRET || 'keyboard cat';

// Your Twilio account SID and auth token, both found at:
// https://www.twilio.com/user/account
//
// A good practice is to store these string values as system environment
// variables, and load them from there as we are doing below. Alternately,
// you could hard code these values here as strings.
cfg.accountSid = process.env.TWILIO_ACCOUNT_SID;
cfg.authToken = process.env.TWILIO_AUTH_TOKEN;
cfg.sendingNumber = process.env.TWILIO_NUMBER;

var requiredConfig = [cfg.accountSid, cfg.authToken, cfg.sendingNumber];
var isConfigured = requiredConfig.every(function(configValue) {
  return configValue || false;
});

if (!isConfigured) {
  var errorMessage =
    'TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_NUMBER must be set.';

  throw new Error(errorMessage);
}

// Export configuration object
module.exports = cfg;
config.js
Server configuration values read at runtime

config.js

You found what you needed from the console - next, let's get your server administrators in a list.

List Your Server Admins and Well-Wishers

Here we create a JSON formatted list of administrators who should be notified if a server error occurs. The only essential piece of data we'll need is a phoneNumber for each administrator.

Loading Code Samples...
Language
[
  {
    "phoneNumber": "+15557778888",
    "name": "Doraj"
  },
  {
    "phoneNumber": "+14447778888",
    "name": "Nivek"
  }
]
config/administrators.json
JSON Administrator list

config/administrators.json

Next, let's have a look at how we're going to capture application exceptions.

Handle All The Unexpected Application Exceptions

We will implement error handling and message delivery as a piece of Express.js middleware.

We'll make all our Twilio API calls from inside our custom middleware.

Loading Code Samples...
Language
var path = require('path');
var express = require('express');
var bodyParser = require('body-parser');
var session = require('express-session');
var flash = require('connect-flash');
var morgan = require('morgan');
var csurf = require('csurf');

var config = require('./config');
var twilioNotifications = require('./middleware/twilioNotifications');

// Create Express web app
var app = express();

// Use morgan for HTTP request logging in dev and prod
if (process.env.NODE_ENV !== 'test') {
  app.use(morgan('combined'));
}

// Serve static assets
app.use(express.static(path.join(__dirname, 'public')));

// Parse incoming form-encoded HTTP bodies
app.use(bodyParser.urlencoded({
  extended: true
}));

// Create and manage HTTP sessions for all requests
app.use(session({
  secret: config.secret,
  resave: true,
  saveUninitialized: true
}));

// Use connect-flash to persist informational messages across redirects
app.use(flash());

// Configure application routes
var routes = require('./controllers/router');
var router = express.Router();

// Add CSRF protection for web routes
if (process.env.NODE_ENV !== 'test') {
  app.use(csurf());
  app.use(function(request, response, next) {
    response.locals.csrftoken = request.csrfToken();
    next();
  });
}

routes(router);
app.use(router);

// Handle 404
app.use(function(request, response, next) {
  response.status(404);
  response.sendFile(path.join(__dirname, 'public', '404.html'));
});

// Mount middleware to notify Twilio of errors
app.use(twilioNotifications.notifyOnError);

// Handle Errors
app.use(function(err, request, response, next) {
  console.error('An application error has occurred:');
  console.error(err.stack);
  response.status(500);
  response.sendFile(path.join(__dirname, 'public', '500.html'));
});

// Export Express app
module.exports = app;
webapp.js
Main application file to load your routes and router configuration

webapp.js

Now you've seen the main server application - let's look in detail at how we'll send SMS notifications to the administrators in case of emergency.

Trigger Notifications for Everyone on the Administrator List

In our Express middleware module, we read all the soon-to-be-awoken administrators from our JSON file and send alert messages to each of them.  We do that using the sendSms method in twilioClient. We also call next with the error object, which will allow other configured middleware to execute after us.

Loading Code Samples...
Language
var twilioClient = require('../twilioClient');
var fs = require('fs');
var admins = require('../config/administrators.json');

function formatMessage(errorToReport) {
  return '[This is a test] ALERT! It appears the server is' +
    'having issues. Exception: ' + errorToReport +
    '. Go to: http://newrelic.com ' +
    'for more details.';
};

exports.notifyOnError = function(appError, request, response, next) {
  admins.forEach(function(admin) {
    var messageToSend = formatMessage(appError.message);
    twilioClient.sendSms(admin.phoneNumber, messageToSend);
  });
  next(appError);
};
middleware/twilioNotifications.js
Wrapper around Twilio REST Client to easily send messages

middleware/twilioNotifications.js

And that's how our notifications will be prepared - let's take a look now at how the server will send the message(s).

Send SMS or MMS Messages from the Server

There are three parameters needed to send an SMS using the Twilio REST API: from, to, and body. US and Canadian phone numbers can also send an image with the message (other countries will have an automatic shortened URL).  Just uncomment mediaUrl and insert your choice of image, serious or funny.

Loading Code Samples...
Language
var config = require('./config');

module.exports.sendSms = function(to, message) {
  var client = require('twilio')(config.accountSid, config.authToken);
  // console.log(client.api.messages.create())
  return client.api.messages
    .create({
      body: message,
      to: to,
      from: config.sendingNumber,
    }).then(function(data) {
      console.log('Administrator notified');
    }).catch(function(err) {
      console.error('Could not notify administrator');
      console.error(err);
    });
};
twilioClient.js
Send a text message

twilioClient.js

And with that, you've seen how easy it is to add powerful administrative features with Twilio!  Let's explore where you might want to look next...

Where to Next? 

At Twilio, we love Node.js - and we have plenty of Node content for you to keep those browser tabs full.

Two-Factor Authentication with Authy and Node.js

Increase the security of your login system by verifying a user's identity using Twilio's Authy.

SMS and MMS Marketing Notifications

SMS and MMS messages are a personal way to engage with users. There are many benefits of marketing directly to phones, but perhaps most of all - SMSes and MMSes are opened at a rate far greater than email.

Did this help?

What are you going to build next?  Hit up the Twilio account on Twitter and let us know!

Kevin Whinnery
Samuel Mendes
Paul Kamp
Jose Oliveros
Andrew Baker
Agustin Camino

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd browsing the Twilio tag on Stack Overflow.

1 / 1
Loading Code Samples...
var dotenv = require('dotenv');
var cfg = {};

if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
  dotenv.config({path: '.env'});
} else {
  dotenv.config({path: '.env.example', silent: true});
}

// HTTP Port to run our web application
cfg.port = process.env.PORT || 3000;

// A random string that will help generate secure one-time passwords and
// HTTP sessions
cfg.secret = process.env.APP_SECRET || 'keyboard cat';

// Your Twilio account SID and auth token, both found at:
// https://www.twilio.com/user/account
//
// A good practice is to store these string values as system environment
// variables, and load them from there as we are doing below. Alternately,
// you could hard code these values here as strings.
cfg.accountSid = process.env.TWILIO_ACCOUNT_SID;
cfg.authToken = process.env.TWILIO_AUTH_TOKEN;
cfg.sendingNumber = process.env.TWILIO_NUMBER;

var requiredConfig = [cfg.accountSid, cfg.authToken, cfg.sendingNumber];
var isConfigured = requiredConfig.every(function(configValue) {
  return configValue || false;
});

if (!isConfigured) {
  var errorMessage =
    'TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_NUMBER must be set.';

  throw new Error(errorMessage);
}

// Export configuration object
module.exports = cfg;
[
  {
    "phoneNumber": "+15557778888",
    "name": "Doraj"
  },
  {
    "phoneNumber": "+14447778888",
    "name": "Nivek"
  }
]
var path = require('path');
var express = require('express');
var bodyParser = require('body-parser');
var session = require('express-session');
var flash = require('connect-flash');
var morgan = require('morgan');
var csurf = require('csurf');

var config = require('./config');
var twilioNotifications = require('./middleware/twilioNotifications');

// Create Express web app
var app = express();

// Use morgan for HTTP request logging in dev and prod
if (process.env.NODE_ENV !== 'test') {
  app.use(morgan('combined'));
}

// Serve static assets
app.use(express.static(path.join(__dirname, 'public')));

// Parse incoming form-encoded HTTP bodies
app.use(bodyParser.urlencoded({
  extended: true
}));

// Create and manage HTTP sessions for all requests
app.use(session({
  secret: config.secret,
  resave: true,
  saveUninitialized: true
}));

// Use connect-flash to persist informational messages across redirects
app.use(flash());

// Configure application routes
var routes = require('./controllers/router');
var router = express.Router();

// Add CSRF protection for web routes
if (process.env.NODE_ENV !== 'test') {
  app.use(csurf());
  app.use(function(request, response, next) {
    response.locals.csrftoken = request.csrfToken();
    next();
  });
}

routes(router);
app.use(router);

// Handle 404
app.use(function(request, response, next) {
  response.status(404);
  response.sendFile(path.join(__dirname, 'public', '404.html'));
});

// Mount middleware to notify Twilio of errors
app.use(twilioNotifications.notifyOnError);

// Handle Errors
app.use(function(err, request, response, next) {
  console.error('An application error has occurred:');
  console.error(err.stack);
  response.status(500);
  response.sendFile(path.join(__dirname, 'public', '500.html'));
});

// Export Express app
module.exports = app;
var twilioClient = require('../twilioClient');
var fs = require('fs');
var admins = require('../config/administrators.json');

function formatMessage(errorToReport) {
  return '[This is a test] ALERT! It appears the server is' +
    'having issues. Exception: ' + errorToReport +
    '. Go to: http://newrelic.com ' +
    'for more details.';
};

exports.notifyOnError = function(appError, request, response, next) {
  admins.forEach(function(admin) {
    var messageToSend = formatMessage(appError.message);
    twilioClient.sendSms(admin.phoneNumber, messageToSend);
  });
  next(appError);
};
var config = require('./config');

module.exports.sendSms = function(to, message) {
  var client = require('twilio')(config.accountSid, config.authToken);
  // console.log(client.api.messages.create())
  return client.api.messages
    .create({
      body: message,
      to: to,
      from: config.sendingNumber,
    }).then(function(data) {
      console.log('Administrator notified');
    }).catch(function(err) {
      console.error('Could not notify administrator');
      console.error(err);
    });
};