cover image for tube status tutorial

Checking the London Tube Status with Nexmo’s SMS API

Published July 31, 2019 by Javier Molina Sanz

Today we’ll be building an application that will allow us to check the status of a given line of the London Underground using the Nexmo SMS API. We are going to leverage the Transport for London (TFL API) to retrieve real-time data about the status of a tube line chosen by the user. The trigger will be an inbound SMS to our Virtual number. Does it sound like a plan? Follow through this tutorial then. We will get the same status as in their website straight into our handset via SMS. This is especially handy if for some reason you don’t have internet access to check Google Maps/Citymapper or if you’ve exceeded your monthly data allowance.

Our application workflow will be something like the following diagram:

sketch diagram of workflow

I know 😌 chances are you don’t live in London, and you may think this tutorial is not relevant for you. However, I truly believe that this is a very illustrative example of what you can build on top of Nexmo.

This tutorial will walk you through all the steps to create this application from scratch. However, if you prefer to get a hold of the finished repository, please go check it out!

Prerequisites

For the first part of the tutorial, we will need:

  • Some basic Javascript/node.js Knowledge.

  • Sign up for a Nexmo account if you haven’t already.

  • Rent an SMS enabled Nexmo virtual number. You can do so by using our Numbers API or via the Nexmo Dashboard.

  • You will need to use ngrok to expose your local server to the internet so Nexmo can reach it. We have a detailed tutorial on this.

If you want to get your application deployed to Heroku, you will also need:

  • A Heroku account (we will only be using the Free-tier).

  • Some basic git commands to deploy our application to Heroku.

Setting up our Project

Create a project folder named Nexmotubestatus on your local machine and change to it.

Let’s create our main file where we’ll store our code. We will also create our .env file where we’ll be storing our Nexmo and TLF credentials as well as some other variables.

The next step is to create the package.json file.

Let’s install and save the necessary dependencies.

We will fill in the .env file with the Nexmo apikey and apiSecret and the TFL app_id ID and app_key. We will also include here our previously purchased Virtual number.

Let’s Start With The Fun Stuff

As always, let’s require all the dependencies at the beginning of our project. We will use the express framework to build our application. We’re going to use the dotenv library so that we work with environment variables. We’ll be using body-parser so we can parse the incoming requests coming from Nexmo’s server.
For the API requests to the TFL API, I chose the request library as I find it quite straightforward but you can use any other such as axios. Lastly and most importantly, 😊 we require the Nexmo library to send the line status back to the user.

Paste the following code into your newly created file. We import all the dependencies installed, and we’ve defined a variable that contains all the accepted line names provided by the TFL API. We don’t want to send a request to the TFL API if the user doesn’t provide a valid line name (I will explain in a bit why all the values are capitalized).
The variable called status will contain any relevant status in relation to the status of the said line. Also, add in the different credentials you’ll need to utilize the Nexmo and TFL APIs respectively. These will be retreived from the .env file:

In the following lines, we’re initiating our application and defining some basic middleware. Note that we have defined the port 3000 for our server to be listening in, but you can choose other. Take into account that there’s some space in between (commented out) that will be filled out with our route for incoming requests:

Let’s define two functions to tidy-up a little bit the code. The first function sendSms() is going to take in two parameters: the phone number of the user and the text to be sent back to the user. We’ll be reusing a little bit of the code.

The second function checkLineStatus() will take in two parameters: the line name and the user’s phone number as we will be sending a message back to the user with the requested information.

If the status of the given line is Good Service (Note that the TFL API will always return this when normal service is running) send this back to the user. Otherwise, It’s important to take into account that when there’s a line disruption, the TFL API will provide a reason within the lineStatus object. That’s what we’re pushing into our array for every disruption occurred (Hopefully none for commuters’ sake 😂). Don’t forget that within this function, we’re also calling the sendSms() function to return the line status to the user in both scenarios.

Lastly, we’re going to fill in our inbound route to listen to incoming messages from the users. Let’s have a look at what an inbound message from Nexmo looks like.

To accomplish our goal, we’re going to need to store two of the above parameters; The text sent by the user (line name) and the user’s number. These will be stored in our new variables (Tube_line and Number_msisdn respectively) as soon as our /inbound route gets hit.

It is important to note that we’re capitalizing the tube line. The reason behind this is that we want to compare a specified String to another String ignoring case considerations (the user can send us Central, CENTRAL or central). By capitalizing the input from the user and comparing it with our lines array (already capitalized) we work around this. Add in the following code in the space we got reserved for our route.

The (lines.indexOf(Tube_Line) > -1) bit will allow us to check if the value stored in Tube_line matches any of the values in the lines array. This method returns a first index at which the given item can be found in an array, or -1 if it is not present in an array. We’ll want only to check the status of a given line if the input matches any of the valid values. Otherwise, we will receive a beautiful HTTP 404 back from the TFL API. Presuming we’re gentle enough to let the user know that they’ve introduced a wrong value, we’ll send them back a message providing them with the valid values. This is done when the indexOf method is equal to -1 as explained above.

Let’s run ngrok in the same port where our local server is listening in my case 3000 or the one specified in the .env file.

ngrok

Now we can configure our virtual number to point to our Inbound SMS webhook URL. Let’s do it via our Numbers API. As per the docs, we can see that we’ll need to make a POST request, authenticating with API key and secret as query parameters. The Content-type must be set to x-www-form-urlencoded and the request body will be formed by three parameters, the moHttpUrl which is the webhook for inbound messages, country is the two character country code in ISO 3166-1 alpha-2 format and msisdn is the number to be updated. Note: Don’t forget to replace it with your own credentials and append /inbound at the end of our ngrok URL as otherwise, it wouldn’t match our express route.

If all went as it should, we have received an HTTP 200 response back from the API. Something like:

You could also update the Inbound SMS settings for your virtual number via Nexmo Dashboard or using the Nexmo CLI but I wanted to look fancier 😜

Alright, it’s time to test this out 🙈. Let’s grab our phone and send an SMS with any line name that matches our lines array to the Nexmo number we’ve just configured. As an example, I will query the name of the line that gets me to work every day.

demo of app performance on phone

💃💃 As you can see, shortly after we send a message to our previously configured virtual number, we are receiving an SMS with the status of the requested line. Well done and thanks for following through that far!

Mocking Inbound Messages

If for some reason you don’t have the chance to use your handset or you don’t want to manually send SMS in order to test the application, we’ve got you covered as well. An inbound message is simply represented as a GET or POST request to your webhook. You can define which method you want Nexmo to use to deliver your inbound messages in your Nexmo Dashboard Settings. I’m using POST for this tutorial.

Taking this into account, we can simulate the behavior of an inbound message by manually hitting our local server exposed with ngrok to see if the application works as it should. I’ll be using POSTMAN but feel free to use any other service of your choice. We’re going to make a POST request to our inbound webhook defining a generic raw JSON body (as the one that Nexmo would send for an inbound message). However, do remember to change the msisdn so that our application knows where to reply. Also, replace the text parameter to play around with different line name values, you can purposely type down an invalid value so that you receive a message containing the permitted values. My API request looks something like this:

view in postman

In this case, the to parameter is not relevant so I set it to a random value. It is important to add the Content-Type header and set it to application/json so that our application knows how to handle this data. As you can see at the bottom-right-hand side, our application returns an HTTP 204 as defined in our /inbound route via the res.status(204).send()

What’s Next? -> Let’s Deploy To Heroku

Heroku is a platform intended to easily deploy your Web application and scale your services according to your needs. They also offer some useful add-ons to simplify some daily tasks.
We are going to leverage Heroku due to the fact that it’s pretty easy to use, and the documentation is great on the Heroku site. By using Heroku we can avoid the hassle of renting, and configuring our server.

The concept of dynos exists within Heroku’s platform. This is nothing but a container where your application will be deployed. Your application usage will consume dyno hours (only when it’s running) but don’t worry as they offer 550 free hours a month out-of-the-box or 1000 hours in case you agree to verify your account by providing a credit card. You can easily scale up or down your application taking into account the traffic demand, but this is out of the scope of this tutorial.

If you’ve never deployed a Heroku application, it may be worth to go through their docs, or at least read the bit where they explain how to deploy a node.js application. To determine how to start your app, Heroku first looks for a Procfile. This is a file that specifies the commands that are executed by the app on startup. If no Procfile exists for a Node.js app, Heroku will attempt to start a default web process via the start script in your package.json.

Let’s edit our package.json, so the part that contains the scripts property has this bit included:

Then, we’re going to create a .gitignore file to ensure that local environment variables, build related output and modules are not committed to the git repository

The only downside of using the free-tier is that once your application idles after 30 minutes of inactivity, it can take a little bit until it awakes (when receiving a new request). Pragmatically, this means that we may see a slight delay when receiving the message back from Nexmo if the application has been inactive for a while. This is because the request to the TFL API will be handled once the application is restarted again. This is acceptable as this application is not time-critical. However, if you don’t find this sufficient, you can move to the paid service and have a dedicated dyno running for your application.

To determine how to start your app, Heroku first looks for a Procfile. If no Procfile exists for a Node.js app, Heroku will attempt to start a default web process via the start script in your package.json. The command in a web process type must bind to the port number specified in the PORT environment variable. If it does not, the dyno will not start.

Double-check that you have the Heroku CLI installed and then run the following commands within your project folder, one at a time:

At this point, we’ve created a new git repository, added all of the changes to our repository and submitted our first commit. Let’s now create our application and deploy it to Heroku:

In these few lines, we’ve created our Heroku app and pushed the changes to Heroku. If everything went as expected, you should have your own application created. They will also provide you with the URL where your app can be found once it has been deployed. Well done!

Lastly, we have to tell our app where to find the environment variables given that we didn’t provide any .env file. Run this command to set the required config variables

You can double-check that these variables were added fine by taking a look at your application settings under Heroku Dashboard. This is what our application looks like in the Heroku dashboard. If we hit on Reveal Config Vars, we’ll see the environment variables configured via the Heroku CLI. dashboard view in heroku

In conclusion, this process was relatively simple! I was able to get this up and running in a matter of a few minutes, which is excellent. All that is left now is to update our number to point to our new webhook, you can simply replicate the steps above (when we configured our number via the Numbers API). As a reminder, don’t forget to include the /inbound at the end of the URL matching the route in our script.

Hopefully, if we send an SMS once we have updated the Inbound Webhook URL for our number, this will work as expected. This is what a disruption status looks like. It seems that it would have been necessary to reschedule our journey if we were traveling to Heathrow via the District Line at the time I was testing this.

sms view of final working app

That’s all for today but if you are willing to continue playing around with our APIs, you may find helpful the following links:

Leave a Reply

Your email address will not be published.