We're live-coding on Twitch! Join us!

Short Message Service (SMS) is not a new technology, and has been around since even before World Wide Web was born. Now with APIs, you can easily integrate SMS with web technologies. With the SMS-enabled web you can build variety of products and services such as user authentication, alerts, notifications, general communication tools and bots.

In this tutorial, I will walk you through how to build a simple web app that sends SMS messages using Node.js, Express, and Nexmo SMS API.

Additionally, to make the web app feel more interactive, let’s also implement W3C Web Notifications API for the front-end UI to display the SMS delivery confirmation message with using Socket.IO!

Sending SMS from Web Demo in GIF animation

You can view the source code on GitHub and run the demo locally to see it in action!

Setting Up Your App

You need to have Node.js installed on your machine to get started! First, create your app directory then set up your app. On terminal:

$ npm init

Then follow the command line instruction to create a package.json file, which defines your app.

Installing Module Dependencies

Now you need to install the modules the app will depend upon. To build a core app, use npm to install nexmo-node, Express.js, body-parser, and ejs. For the additional feature, get Socket.IO too.

$ npm install nexmo express body-parser ejs socket.io --save

Creating App Structure

Create some directories for the files you are going to add. This is all up to you, but here is the example I am using in this tutorial:

├── public
│   ├── css
│   │   └── style.css
│   ├── images
│   │   └── icon-nexmo.png
│   └── js
│       └── app.js
├── server
│   └── index.js
└── views
│   └── index.html
└── package.json

Serving the Web App with Express

Now, let’s setup a HTML web page that serves out a form with a phone number field and a message field using Express, which is a robust web application framework for Node.js.

Create an index.js file:

'use strict';

const express = require('express');
const app = express();
const server = app.listen(4000);

Also, use body-parser (the middleware to parse form input onto a body property of the request) and ejs (HTML templating tool) to configure the Express app:

const bodyParser = require('body-parser');
const ejs = require('ejs');

app.set('views', __dirname + '/../views');
app.set('view engine', 'html');
app.engine('html', ejs.renderFile);
app.use(express.static(__dirname + '/../public'));
app.use(bodyParser.urlencoded({ extended: true }));

Serving HTML:

app.get('/', (req, res) => {

User Interface HTML

Now create an index.html under /views to build the user-interface. Make sure you include a JavaScript file, js/app.js in <script>.

In the html file, create a few form input fields with a button:

<input type="tel" name="number" placeholder="15551231234">
<input type="text" name="text" placeholder="Hello">
<input type="button" value="Send SMS">

Notice the type attributes in each <input> element- For phone number, use type="tel", and for the message text, use the regular type="text". By setting right input type, browsers on touch devices automatically pops up a right type of keyboard for an improved user experience. For instance, when the browser sees type="tel", it serves a number pad.

The entire HTML looks like this:

<!doctype html>
<html lang="en">
  <meta charset="utf-8">
  <title>SMS Web App Demo</title>
  <link rel="stylesheet" href="css/style.css">
  <header> <h1>Text Anybody</h1></header>
  <section id="main" role="main">
    <p class="intro">Enter a number, begins with a country code</p>
      <input type="tel" name="number" placeholder="15551231234" required>
      <input type="text" name="text" placeholder="Hello">
      <input type="button" value="Send SMS">
  <p class="response"></p>

  <!-- Socket.io: This is optional. will be explained later! -->
  <script src="/socket.io/socket.io.js"></script> 
  <!-- Your JS file -->
  <script src="js/app.js"></script>

To see the entire markup and CSS, please refer the source code on GitHub.

Handling the Form Submission

Create app.js in /public/js.

First, get the DOM object for each form input elements:

var numberField = document.querySelector('input[name=number]');
var textField = document.querySelector('input[name=text]');
var button = document.querySelector('input[type=button]');
var msg = document.querySelector('.response');

Note: Notice that I am using ES6 for Node.js, while using ES5 for the front-end code, because not every ES6/ES2015 feature has been supported by modern browsers yet. If you would like to use ES6 all the way, I recommend Babel. However I am not covering it in this article.

Then, listen to the events. Let’s make the form submittable by either hitting a Return key or pressing the button:

textField.addEventListener('keyup', function(e) {
  if ((e.keyCode || e.charCode) === 13) send();
}, false); // when a user presses a Return key

button.addEventListener('click', send, false); // when a user click the "Send" button

Now, let’s define the send function to submit the input values to the Node code. First, grab the values from the inputs.

function send() {
  var number = numberField.value.replace(/\D/g,''); // Remove all non-numeric chars
  var text = textField.value;
  // ... will send the form using fetch here

Next, post the values to server using the Fetch API.

So Fetch! Sending Form via Fetch API

For last decade or so, we have been using XMLHttpRequest for AJAX requests But now is the time to say good-bye to the good old XHR, and say hello to this shiny new Fetch API to make requests to your server!

Now, POST the phone number and the message text as JSON using the Fetch API:

fetch('/', {
  method: 'post',
  headers: {
    'Content-Type': 'application/json'
  body: JSON.stringify({number: number, text: text})
.then(function(res){ console.log(res) })
.catch(function(error){ console.log(error)});

As of 2016, if you want to support Safari version 10 or older, you still need to use XHR as the fallback for now.

Receiving the Form Values with Express

Now going back to the server-side index.js, and add these lines of code to take the form input values from the request:

app.post('/', (req, res) => {
  let toNumber = req.body.number;
  let text = req.body.text;

 // Sending SMS via Nexmo ---

Let’s run this code once and see if this works fine.

$ node server/index.js

Then, go to http://localhost:4000 in your browser. Try sending something.

Once the form is posted successfully, you should see the console.log value like this on terminal:

{ number: '14155551234', text: 'Hello!' }

Next, let’s use Nexmo SMS API to actually send messages to the phone number!

Using the Nexmo SMS API

The Nexmo SMS API allows you to send and receive a high volume of SMS anywhere in the world. Once you get your virtual phone number, you can use the API to manage outbound messages ("sending") and inbound messages (“receiving”). In this app, the API will be used for outbound messages.

To get started with SMS, sign up for a Nexmo account to get your virtual number, as well as your API credentials. Once you signed up, go to your Dashboard to get your Nexmo virtual number on Numbers, and API key and secret on Settings section.

Nexmo Dashboard

Using the Nexmo REST API Client for Node.js

You should have nexmo-node installed for your web app in the very beginning of this tutorial, but if not, install with npm.

$ npm install nexmo --save

In your index.js, initialize with your credentials:

const Nexmo = require('nexmo');
const nexmo = new Nexmo({
  apiKey: API_KEY,
  apiSecret: API_SECRET,
}, {debug: true});

Sending an SMS Message

Then, in your POST method route that you have defined earlier, grab a phone number and a message from the web form, and send an SMS to the number from your Nexmo virtual number (NUMBER):

app.post('/', (req, res) => {
  const toNumber = req.body.number;
  const text = req.body.text;
    NUMBER, toNumber, text, {type: 'unicode'},
    (err, responseData) => {
      if (err) {
      } else {
        // Optional: add socket.io -- will explain later

Now, try sending an SMS to a real phone number (or your Google Voice number) using your app! If everything works, you should receive SMS messages! Try emoji too. When you specify the SMS message type to "unicode" you are able to send and receive unicode including emojis!

SMS sent

You can stop this tutorial right here, or proceed to add extra features!

Extra: Making the App More Interactive with Socket.io and Web Notifications UI

The Nexmo SMS API returns a payload that indicates if the result of the request. Now, let’s add more features to display the sent status UI. (Note: This status indicates that the SMS is successfully sent by you via Nexmo, and not an actual delivery receipt from the recipient's carrier. To access the actual delivery receipt, read the tutorial on Nexmo.com.)

What you are going to do is:

  1. Pass the API response data from the server to the UI with Socket.IO
  2. Display the data on a desktop notification with Web Notifications API

Displaying a Server Response on Browser with Socket.IO

To display the SMS confirmation sent from server on browser, you are going to use Socket.IO so that the server can talk to browsers. You need to use both server API and client API to be able to communicate in real-time.

Server Side

You should have installed Socket.io at the beginning of the tutorial so let’s go ahead and initialize a socket.io instance in index.js:

const socketio = require('socket.io');
const io = socketio(server);
io.on('connection', (socket) => {
  socket.on('disconnect', () => {

Socket.IO lets you emit and receive any events you want. Let’s emit an event in the sendSms callback, where you see the comment that says, // Optional using emit(eventName, eventData):

let data = {id: responseData.messages[0]['message-id'], number: responseData.messages[0]['to']};
io.emit('smsStatus', data);

In the code sample above, you are emitting the message-id, an ID of the submitted SMS message and the phone number your request was sent to. These data come from the Nexmo SMS API response, and the response also gives you more information of the status of the request and costs of the SMS.

Client Side

Now, you need to make browsers receive the event from the server. First, include the socket.io.js in index.html:

<script src="/socket.io/socket.io.js"></script>

Then in the client, app.js, receive the smsStatus event:

var socket = io();
socket.on('smsStatus', function(data) {
  displayStatus('Message ID ' + data.id + ' successfully sent to ' + data.number);

Next, write the displayStatus function that displays the SMS callback data. You can just print it out as a simple HTML text in the browser, however, let’s display it as a desktop notification.

Displaying Data from the Server in Desktop Notifications

The W3C Web Notifications API allows web browsers to display notifications, even when the browser window is in background. Since this feature is supported (or not) by browser vendors, each browser has slightly different UI.

Web Notifications

Each browser provides a built-in permission UI for the API, so that the user can control which web pages can send them notifications. When the page is loaded for the first time, a browser asks a user the permission. Your page can send notifications only after the user has granted permission.

Web Notification permission

To request permission, add these lines in your app.js,

Notification.requestPermission().then(function(status) {
  console.log(status); // when a user granted, status == 'granted', otherwise, 'denied'

Then create a displayStatus function that passes the data received via Socket.IO. In the function, instantiate a new notification object with the contents including an icon:

function displayStatus(message) {
   var notification = new Notification('Nexmo', {
     body: message,
     icon: 'images/icon-nexmo.png'

The source code on GitHub includes the fallback, in case a user denied the permission.

Awesome! Now you have finished building a web app that allows you to send SMS messages, get the result callback from server and display it as a native web notification!


Also read more open-web articles at my personal site, Girliemac.com :-)

Like this article? Follow @girlie_mac on Twitter