JavaScript
Article

Connecting to the Jawbone UP API with Node.js

By Patrick Catanzariti

As a developer, I cannot help but want to access the huge amount of step count and sleeping habit data my Jawbone UP has on me. There is so much data! So I began looking into how to pull in this data using the Jawbone UP API and Node.

I found examples of how to work with the Jawbone UP API and Node across the web but they were all quite involved and had a lot of moving parts, along with some outdated modules (like older versions of Express). In this article I want to focus on the absolute basics—no worrying about saving user data into a database, creating accounts or connecting up social logins. We’ll focus on the core things you need to know to get a Node server to authenticate with the Jawbone API and return user data.

All of the code for this demo is available on our GitHub repo.

Setting up a Jawbone App

The first thing we’ll need is a new Jawbone app set up under our Jawbone account. This is the app which users will be authorizing to access their data.

Start by logging into the developer section of the Jawbone site by going to https://jawbone.com/up/developer and clicking the “Sign In” link on the bottom left. You won’t need a specific Jawbone developer account as they will allow you to log in using an existing Jawbone account.

The Jawbone Developer Sign In

Once logged in, head to https://jawbone.com/up/developer/account, or click the “Manage Account” link in the left hand menu under “Account”.

Manage Account

On this page, you’ll reach your developer account page. From here, click “Create App”.

The Developer Account Page

On the page that loads you will be prompted to enter the details of your app:

  • Name – The name of your application, I entered “Jawbone UP Node Demo”.
  • Description – This is a short description that will come up on the user’s UP App Gallery.
  • Long Description – This comes up on the detail page of the app in the app gallery.
  • Logo – Upload a logo for your application. If you receive an error about “Select” (strange I know, but it’ll make sense to the few people who follow along and get that message), chances are your logo image is too big.
  • URL – Your app’s homepage
  • Authorization URL – The URL that your login page will be found at. For our test purposes, enter in https://localhost:5000/login/jawbone.
  • OAuth Redirect URIs – URLs that your application is allowed to redirect to once the user has been authenticated. In our demo, we’ll enter in https://localhost:5000.

Once you click to create the application, you’ll be directed to the page with a list of your apps. Your newly created app should look similar to mine:

The newly created app

Take note of the “Client Id” and “App Secret” – these are what you’ll need to connect up to the Jawbone API.

Starting our Node App

I’ll be including all our Node server code in one file called server.js. We start by requiring the necessary npm modules for our server.

First, we set up a basic Express app.

var express = require(‘express’),
    app = express(),

We then require ejs (Embedded JavaScript), which allows us to insert JavaScript into our HTML templates. We’ll use this to display JavaScript variables within our returned HTML.

ejs = require('ejs'),

In order to be able to authenticate with the Jawbone API and redirect back to our application, Jawbone requires that we redirect to a page over https. To do this, we’ve got to include https.

https = require('https'),

Next, we include fs, which allows us to read the file system. We’ll need this to read in server certificate files to enable https.

fs = require('fs'),

We’ll also need body-parser to enable us to handle JSON requests:

bodyParser = require('body-parser'),

The Jawbone UP API uses the OAuth 2.0 protocol for authentication. Basically, this means in order to have a user sign in with their Jawbone account and give us permission to access their data, we need to go through this protocol. Luckily, npm’s passport module contains a module called passport-oauth which supports this. We set up passport in our app along with OAuth 2.0 like so:

passport = require('passport'),
JawboneStrategy = require('passport-oauth').OAuth2Strategy,

We’ve then got a self explanatory variable that stores the port we’ll be running on.

port = 5000,

Next up, we will be storing all the values needed for authentication in Passport and OAuth 2.0 inside jawboneAuth. This is the moment you’ll be using the “Client Id” and “App Secret” values we took note of earlier when we registered our app.

jawboneAuth = {
  clientID: 'jUvu1_4u_mA',
  clientSecret: '8961265d16ac678948006c2944ed85dbeeaab547',
  authorizationURL: 'https://jawbone.com/auth/oauth2/auth',
  tokenURL: 'https://jawbone.com/auth/oauth2/token',
  callbackURL: 'https://localhost:5000/sleepdata'
},

Here is an overview of what these values mean and/or where they’ve come from:

  • clientID – this is the “Client Id” listed for your Jawbone app.
  • clientSecret – this is the “App Secret” value below it.
  • authorizationURL – this is the location of the UP OAuth 2.0 authentication page that the user will be redirected to.
  • tokenURL – this is the URL in the Jawbone UP API that we must make a HTTPS call to, in order to request an access token. This token is what we need to include in our calls to the Jawbone UP API to prove we are authorized to make those requests for data. In the Jawbone UP API, this token lasts for a year, so you can store it in a database and have a user connected to their Jawbone account for a year before needing to reauthenticate them. We won’t be looking at storing users and such in this tutorial, but it is good to keep in mind if you’re looking to push this further.
  • callbackURL – the URL on our site which Jawbone will direct the user back to once they’ve successfully given us access to their data. For us it is a page to display the sleep data.

The last variable we’ve got to define is our sslOptions which contains all the details we need to provide to our server to allow us to run this server using HTTPS. I’ll go over each of these in detail later in this article when explaining how we set up HTTPS.

We then include a few lines defining some basic Node app functionality which will be familiar to Node developers out there:

app.use(bodyParser.json());
app.use(express.static(__dirname + '/public'));
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
  • bodyParser – allows us to parse JSON objects.
  • Static folder – defines where our static files like images will be on the server (in our case, /public folder).
  • EJS – assigns the ejs module as our template engine.
  • Views folder – defines where our ejs view files will be on the server (in our case, the /views folder).

In order to initialize Passport within Express, we run the following line:

app.use(passport.initialize());

As a note, there is more to set up in Passport if we wanted to have persistent login sessions. In this case, we would need to set up sessions. However, for this tutorial, we’ll just focus on the initial stage of getting the data down from the Jawbone UP API and won’t worry about login sessions.

Setting up Our GET Requests

In order to direct the user to a login screen for the Jawbone UP API, we need to assign a URL on our server that will redirect to the Jawbone login screen. The code below assigns the URL of /login/jawbone for this purpose. On a GET request to this URL, we call passport.authorize() to bring up our Jawbone UP authorization page:

app.get('/login/jawbone', 
  passport.authorize('jawbone', {
    scope: ['basic_read','sleep_read'],
    failureRedirect: '/'
  })
);

As you can see above, we’ve got an array of specific permissions that we’re asking for — ['basic_read','sleep_read']. In our case, we’re asking for basic user data and sleep data. If you wanted to request access to the step count, meals eaten and so forth, you can add additional permission requests to that array. You can see a list of what is available and what they provide access to at the Jawbone UP Developer Authentication documentation page.

Also note, if there is a failure in authenticating in the Jawbone UP Authentication screen, it’ll redirect us back to the homepage. It is possible in the passport module to set successRedirect too, however I’ve found with the Jawbone UP API, it is not needed as we will define our callback URL in the JawboneStrategy further down in this code.

We then set up the GET request that our sleep data will appear on. This is the location which we’ll be telling the API to redirect us to when we’ve got access to the user data. In this example, it is /sleepdata:

app.get('/sleepdata',
  passport.authorize('jawbone', {
    scope: ['basic_read','sleep_read'],
    failureRedirect: '/'
  }), function(req, res) {
    res.render('userdata', req.account);
  }
);

We have the same passport.authorize() function here, just to check that the user is logged in by the time they reach this page. If so, we run res.render('userdata', req.account); which passes in the data that the Jawbone UP API returned to the userdata.ejs template (which we will set up soon). If they are not logged in, they’ll be directed back to the Jawbone UP authentication screen.

We then set a URL to allow the user to logout at /logout, which redirects the user back to the homepage once logged out:

app.get('/logout', function(req, res) {
  req.logout();
  res.redirect('/');
});

Finally for our routing, we set it to load our index.ejs template if someone tries to access the homepage:

app.get('/', function(req, res) {
  res.render('index');
});

Using Passport to Connect to the Jawbone UP API

The biggest chunk of code is also the most important — setting up a Passport “strategy” to tell Passport how to handle requests to authorize using 'jawbone'. It looks like this:

passport.use('jawbone', new JawboneStrategy({
  clientID: jawboneAuth.clientID,
  clientSecret: jawboneAuth.clientSecret,
  authorizationURL: jawboneAuth.authorizationURL,
  tokenURL: jawboneAuth.tokenURL,
  callbackURL: jawboneAuth.callbackURL
}, function(token, refreshToken, profile, done) {
  var options = {
        access_token: token,
        client_id: jawboneAuth.clientID,
        client_secret: jawboneAuth.clientSecret
      },
      up = require('jawbone-up')(options);

  up.sleeps.get({}, function(err, body) {
    if (err) {
      console.log('Error receiving Jawbone UP data');
    } else {
      var jawboneData = JSON.parse(body).data;

      for (var i = 0; i < jawboneData.items.length; i++) {
        var date = jawboneData.items[i].date.toString(),
            year = date.slice(0,4),
            month = date.slice(4,6),
            day = date.slice(6,8);

        jawboneData.items[i].date = day + '/' + month + '/' + year;
        jawboneData.items[i].title = jawboneData.items[i].title.replace('for ', '');
      }

      return done(null, jawboneData, console.log('Jawbone UP data ready to be displayed.'));
    }
  });
}));

Let’s go over what all of this code is doing.

First, we set up our clientID, clientSecret, authorizationURL, tokenURL and callbackURL from our jawboneAuth object we defined at the start of the file. This is done using new JawboneStrategy().

Next we have our callback function which handles this data. We use the token and done values in this callback function. token is the Jawbone UP API access token that we’ll need to include with any calls to the API to prove we’re authenticated. done is the callback function that returns our data to the app.

We pass in the access token, along with the client ID and secret defined earlier, into the jawbone-up module within the options object:

var options = {
      access_token: token,
      client_id: jawboneAuth.clientID,
      client_secret: jawboneAuth.clientSecret
    },
    up = require('jawbone-up')(options);

The jawbone-up module is the Node module that gives us access to the Jawbone UP API endpoints. These are the calls we make to the API to return user data (e.g. GET https://jawbone.com/nudge/api/v.1.1/users/@me/sleeps), however the jawbone-up module allows us to access these in functions such as up.moves.get() and up.sleeps.get(). In our example, we’ll be using up.sleeps.get() to get sleep data.

Within up.sleeps.get() we’ve got two variables, err and body. If there’s an error in receiving the data from the API, it’ll be returned in the err variable so we test for that at the start of our callback.

Otherwise, we’ve got our data returned in a JSON string in the body variable. The body variable will contain a JSON string of values that’ll look like so:

{
  "meta": {
    "user_xid": "Hllksn238c-KJBu2esff_Q",
    "message": "OK",
    "code": 200,
    "time": 1428562859
  },
  "data": {
    "items": [
      {
        "time_updated": 1428534140,
        "xid": "8060gi-3V-kLT-niK4ZxB2NLqnct9_2B",
        "title": "for 7h 45m",
        "time_created": 1428504300,
        "time_completed": 1428533100,
        "details": {
          "body": 0,
          "sound": 15000,
          "tz": "Australia/Sydney",
          "awakenings": 0,
          "light": 12900,
          "mind": 0,
          "asleep_time": 1428505800,
          "awake": 1500,
          "rem": 0,
          "duration": 28800,
          "smart_alarm_fire": 0,
          "quality": 84,
          "awake_time": 1428533100,
          "sunrise": 1428524040,
          "sunset": 1428565320
        },
        "date": 20150409,
        "shared": true,
        "sub_type": 0
      },
      {
        "time_updated": 1428447559,
        "xid": "8060gi-3V-nmNeDAWAAXjwzpZx2RQOgg",
        "title": "for 7h 38m",
        "time_created": 1428418945,
        "time_completed": 1428447488,
        "details": {
          "body": 0,
          "sound": 13985,
          "tz": "Australia/Sydney",
          "awakenings": 1,
          "light": 13501,
          "mind": 0,
          "asleep_time": 1428419639,
          "awake": 1057,
          "rem": 0,
          "duration": 28543,
          "smart_alarm_fire": 0,
          "quality": 78,
          "awake_time": 1428447300,
          "sunrise": 1428437580,
          "sunset": 1428478980
        },
        "date": 20150408,
        "shared": true,
        "sub_type": 0
      }
    ],
    "links": {
      "next": "/nudge/api/v.1.1/users/Hllksn238c-KJBu2esff_Q/sleeps?page_token=1427987112334&limit=10"
    },
    "size": 10
  }
}

Everything we’ll want is within data. We parse the values above into a JavaScript object using JSON.parse(body) and assign the values in the data key to a variable called jawboneData:

var jawboneData = JSON.parse(body).data;

Then, we have a for loop that goes through each item in the array within data and formats our date and sleep times before returning them to our template to display.

var date = jawboneData.items[i].date.toString(),
    year = date.slice(0,4),
    month = date.slice(4,6),
    day = date.slice(6,8);

jawboneData.items[i].date = day + '/' + month + '/' + year;

Here, we read in the date, convert it to a string and then slice out the day, month and year ourselves. It is returned as a value of 20150408, so we slice out the first four digits as the year, the two after that as the month and the last two as the day. We then arrange it so that it will be DD/MM/YYYY, if you’d prefer to format it in the US date format, you can switch the month and day:

jawboneData.items[i].date = month + '/' + day + '/' + year;

The Jawbone API returns a relatively nicely formatted sleep duration value as the title which looks like this: "for 9h 43m". We can use this but remove the "for " part like so:

jawboneData.items[i].title = jawboneData.items[i].title.replace('for ', '');

We then return that data to our Passport’s callback function that’ll render our userdata.ejs. To do so, we return our jawboneData variable to the done function. There’s also a console.log just so we can see in the log when the Jawbone UP data has been sent to be displayed:

return done(null, jawboneData, console.log('Jawbone UP data ready to be displayed.'));

Using HTTPS

As I mentioned earlier, in order to use the Jawbone UP API, we need to be running our server with HTTPS, as Jawbone’s service requires both sides to be running HTTPS. If the callbackURL is not set to https you’ll receive “Invalid redirect” errors when you try to log in.

In order to get our example to work, we’ll be using a self signed certificate. If you are doing this on a live site, you’ll want to get proper certificates from a valid certificate authority.

In server.js, we have defined two SSL options:

sslOptions = {
  key: fs.readFileSync('./server.key'),
  cert: fs.readFileSync('./server.crt')
};

These are the file locations on our server of our two authentication related files:

  • key – This is the private key for our server
  • cert – This is our self signed certificate

Generate a Private Key for Our Server

To generate a private key we’ll need to use the OpenSSL Toolkit. Mac OSX and Linux users should have this pre-installed. For Windows users, you can install Cygwin, search for “openssl” at the “Select Packages” screen and choose the package that appears.

We can generate that private key by opening our terminal, navigating to the folder for our server and running the following command:

openssl genrsa -out server.key 2048

This generates a private server key ready for use called server.key.

Generate a Certificate Signing Request (CSR)

We then need to generate a CSR. This would usually be sent off to a certificate authority but in our case we’ll be signing it ourselves for testing purposes.

To generate a CSR using our private key we created above, run the following command:

openssl req -new -key server.key -out server.csr

You will be given a list of questions to answer, answer these and you’ll receive your CSR as a file called server.csr.

Generate a Signed Certificate Using Our Server Private Key

Finally, to generate a self-signed certificate without a certificate authority, we run the following command to generate a certificate that’ll be valid for one year:

openssl x509 -req -in server.csr -out server.crt -signkey server.key -days 365

That command should have generated a server.crt file — this is your certificate.

Remove Our Certificate Request

For those who like to keep things tidy and are self-signing their certificate, we can remove server.csr as our certificate is now signed.

We’re HTTPS Ready

With our private key and certificate ready and defined in our Node file, our server is ready to run as HTTPS. The following code starts the server using HTTPS and our sslOptions:

var secureServer = https.createServer(sslOptions, app).listen(port, function(){
  console.log('UP server listening on ' + port);
});

Our EJS files

Our HTML for this app is all in .ejs files so that we can include JavaScript variables inside them when needed. These files are all within /views. index.ejs is very simple and just contains a title, instructions and a log in button that’ll go to /login/jawbone:

<body>
  <h1>Jawbone UP Sleep Data</h1>
  <p>Log in to see your latest sleep data.</p>
  <a href="/login/jawbone" class="btn">Login</a>
</body>

userdata.ejs is where the action is. The main bit we can focus on is our table:

<table class="sleep-table">
  <thead>
    <tr>
      <th>Date</th>
      <th>Sleep time</th>
    </tr>
  </thead>
  <tbody>
  <% for (var i=0; i<items.length; i++) { %>
    <tr>
      <td><%= items[i].date %></td>
      <td><%= items[i].title %></td>
    </tr>
  <% } %>
  </tbody>
</table>

For those new to EJS, we embed JavaScript within <% and %> tags.

We’re passing in items to the userdata template, which we iterate through using a for loop like so: <% for (var i=0; i<items.length; i++) { %>.

Each date and title is then inserted into our HTML using <%= items[i].date %> and <%= items[i].title %>.

Our App in Action

To get the app running, head to your terminal and run:

node server.js

With it running, go to http://localhost:5000 and you’ll see our initial page:

Our initial page

If we click the login button, we’ll be taken to http://localhost:5000/login/jawbone, which will direct us to the Jawbone UP authentication page. The page will prompt us for our Jawbone login details. Once you enter those details or if you’re already logged into the Jawbone site, you’ll be directed to an Auth page requesting access to your user data. Click “Agree”:

Login details found

When we click agree, we should be directed back to the http://localhost:5000/sleepdata page with a table of our sleep data returned:

Our sleep data page

And if we click the “Log out” button, it should log us out and redirect us back to the home page.

Conclusion

That completes our overview of the basics of connecting to the Jawbone UP API and returning data to a Node server.

The next steps from here could include setting up a database to store the data for future use, creating user accounts for your app, expanding the amount of data you pull in from the UP API, changing how it displays (maybe adding some pretty graphs!) and more. Combine this data with any number of other APIs out there and the potential for some really neat applications is huge!

Other useful resources

Recommended
Sponsors
Get the latest in JavaScript, once a week, for free.