Office 365 APIs and Node.js

UPDATE: The node-outlook library that is discussed in this post has been updated with a new interface that does not use the Cordova library and is much simpler to use. It's recommended that new apps use the newer interface. The tutorial linked at the end of the article has been updated to use the newer interface.

When we launched the Office 365 APIs, the Visual Studio folks released some very helpful client-side implementations of these APIs, making it much easier to get started. One of these implementations, the Microsoft Office 365 APIs Client Libraries for Cordova Applications, got a lot of attention from JavaScript developers. However, there was some confusion about the library and what it was designed to do. As the name suggests, this library was created to be used in applications built on the Cordova platform, and more specifically, for Multi-Device Hybrid App projects in Visual Studio. Developers who tried using it in a web app soon ran into issues. It just wouldn't work.

With all of the interest in using this library from a web app, when I was asked if we could modify the library to make it work in Node.js, I said "why not?"

Finding the problem

As it turns out, the big stumbling block is that the library uses the AJAX XMLHttpRequest object to send API requests. AJAX is all client-side, and not available to server-side platforms like Node.js. So it makes sense that it doesn't work in web apps.

The solution

Luckily, someone clever already provided a solution for this. The XMLHttpRequest for node.js module sounds like exactly what we need! If we can use this, we can avoid having to change the Cordova library much at all.

With that out of the way, the question left is how to load this non-Node module. As before, someone clever has already found the solution to that. With this trick in my toolbox, I decided to write a Node module to wrap the Cordova libraries (to be specific, just the Mail, Calendar, and Contacts APIs).

The node-outlook module

The code for the module was incredibly simple. All I needed to do was create an index.js file and put the following in it:

  var fs = require("fs");
 var path = require("path");
 var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
 
 var currentDirectory = path.dirname(fs.realpathSync(__filename));
 
 exchangefile = fs.readFileSync(path.join(currentDirectory,
   './exchange-lib/exchange.js'), 'utf8');
 eval(exchangefile);
 utilityfile = fs.readFileSync(path.join(currentDirectory,
   './exchange-lib/utility.js'), 'utf8');
 eval(utilityfile);
 
 exports.Microsoft = Microsoft; 

The exchange.js and utility.js files were copied straight from the Cordova library NuGet package. Pretty simple right? Well, it's even simpler now! I published node-outlook on the NPM registry, so you can save yourself the hassle and install via NPM. Or if you prefer, get the code on GitHub.

Using node-outlook

For a full sample that uses this library, check out node-mail. I'll cover the basic requirements here.

In order to use the library, you need to implement a callback function that returns an OAuth2 access token. The OutlookServices.Client class takes this callback as a parameter to its constructor. Whenever the client class makes a call to the Office 365 service, it calls this function to get the token. How you implement the callback will depend on how you're storing and managing tokens. As an example, let's look at how node-mail does it.

  function getAccessToken(resource, session) {
   console.log("getAccessToken called for " + session);
   var deferred = new outlook.Microsoft.Utility.Deferred();
   if (session.token.expired()) {
     session.token.refresh(function(error, result) {
       if (error) {
         console.log("Refresh token error: ", error.message);
       }
       session.token = result;
       console.log("NEW ACCESS TOKEN: ", session.token.token.access_token);
       deferred.resolve(session.token.token.access_token);
     });
   }
   else {
     // Return the token right away
     console.log("EXISTING ACCESS TOKEN: ", session.token.token.access_token);
     deferred.resolve(session.token.token.access_token);
   }
   return deferred;
 }
 
 function getAccessTokenFn(resource, session) {
   return function() {
     return getAccessToken(resource, session);
   }
 } 

The node-mail app caches tokens in the session. The getAccessToken function returns the cached token if it's not expired. Otherwise, it refreshes the token and returns the new token. The getAccessTokenFn function exists to wrap getAccessToken as a function with no parameters, which is what the OutlookServices.Client class expects.

With authorization taken care of, using the library works just like it does in a Cordova app. For example, you could get messages in the inbox with the following code:

  var outlookClient = new outlook.Microsoft.OutlookServices.Client(
   'https://outlook.office365.com/api/v1.0', 
   authHelper.getAccessTokenFn('https://outlook.office365.com/', session));
 
 outlookClient.me.messages.getMessages()
 .orderBy('DateTimeReceived desc').fetchAll(10).then(function (result) {
   result.forEach(function (message) {
     console.log(message.subject);
   });
 }, function (error) {
   console.log(error);
 }); 

For a step-by-step tutorial, see https://dev.outlook.com/RestGettingStarted/Tutorial/node.

As always, I'd love to hear your feedback in the comments or on Twitter (@JasonJohMSFT). Feel free to report issues or submit pull requests on GitHub!