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!

Comments

  • Anonymous
    March 20, 2015
    Thanks for the wonderful post Jason!!! I got the setup working for my Office 365 account; but it didn't work for other users. Below are the steps I did,
  1. Created Office 365 Developer account
  2. Thought Azure AD account will be automatically created; but the system said I need to have "Azure Subscription"
  3. I purchased trial version of "Azure Subscription"
  4. Followed all the steps you have mentioned in "github.com/.../RegisterAnAppInAzure.md"
  5. App is registered and I am able to access Office 365 REST API's (calendar,events) using my user account
  6. Using OAuth2 Auth flow, I tried accessing as a different user (who has Office 365 Home account) , token and refresh token was returned. But, with this token if I access the Office 365 REST API, I am getting "401 Unauthorized Error".
  7. I went to my Active Directory portal; added this user explicitly and gave "User" role
  8. Now when I accessed Office 365 REST API, 401 error is gone. But, getting "ErrorMissingEmailAddress" error Do I need to explicitly add all the users who wants to access their Office 365 data via "App" that I registered in "Active Directory"? I am accessing "Office 365 REST API" via OAuth2 in my NodeJS application. My NodeJS application is not multi-tenant application and so I cannot have user organization Active Directory synced to my AD portal. Any users registered with our NodeJS application, needs "Sign-In with Azure/Office" option, so they can view their Office 365 data. Please let me know if I am missing any configuration.
  • Anonymous
    March 22, 2015
    Ah. Office 365 Home Accounts are currently not supported by the APIs. This is on the roadmap, but we don't have a firm date to share yet.

  • Anonymous
    March 26, 2015
    Thanks for the update Jason. Is Office 365 Business Account (Business/Business Essentials/Business Premium) supported by the APIs?

  • Anonymous
    March 26, 2015
    It should be. You can always try an account by going to apisandbox.msdn.microsoft.com and clicking on "My Office 365". It will let you sign in and try simple GETs against your account.

  • Anonymous
    May 05, 2015
    Hey, Jason your update was really helpful & informative. Thanks for this amazing update & looking forward for your next post.

  • Anonymous
    June 25, 2015
    Hi Jason. The scenario you described here works, but it doesn't match my requirements for an application. I am talking about Daemon type of application. The one without user interaction. Have you ever built those using NodeJS/node-outlook?

  • Anonymous
    June 25, 2015
    No, I haven't done it personally with Node. It should work, you will just have a slightly different auth flow, and you would use the .users accessor instead of .me.

  • Anonymous
    July 29, 2015
    Hey Jason, Appreciate your effort. I am trying to send mail using the outlookClientObject. outlookClient.me.sendMail(message) My code never sends off an email. I am still able to do the follwoing things, Get Mail Delete Mail Create a Calendar Event Read Contacts Add Contacts.. I am not able to send an email to someone from the code. Any assistance would be greatly Appreciated!!

  • Anonymous
    July 29, 2015
    Ravi: Sorry to hear that. Make sure you have the "Send mail" permission specified in your app registration. If you do and you still have trouble, post details on Stack Overflow and tag your post with the "outlook-restapi" tag so my colleagues and I will see it.

  • Anonymous
    August 26, 2015
    Hi Jason, Thanks for the post, appreciate your effort. Are you aware of any official JS client for EWS Managed APIs and not just Office365

  • Anonymous
    December 08, 2015
    Hey Jason, Do I need to submit my app for approval others to access?

  • Anonymous
    December 08, 2015
    @Anil: Nope!

  • Anonymous
    January 26, 2016
    Jason, I was super-pumped to find your blog article.  I want to try automating some regular calendar tasks, and this looks like the perfect place to get started! One problem - the links at the bottom of your article ('Common calendar tasks using an Office 365 client library' in particular) don't work.  Where can I find this content now?

  • Anonymous
    January 26, 2016
    @Tim: Oh yeah, sorry about that. The original version of the Node library used an old Cordova Exchange library that got retired. That link was pointing at the old docs for that library, but they pulled that when they retired it. So I basically redesigned a v2 interface for the Node library (node-outlook: www.npmjs.com/.../node-outlook) that doesn't use the Cordova stuff and is a little more straightforward to use. Check this tutorial: dev.outlook.com/.../node

  • Anonymous
    April 10, 2017
    Hello Jason,I'm creating new NodeJs project. Customer wants to use Office365 mail service.All works fine but I have a question. We can login with Office 365 or Outlook.com account and grant access to our app.Can I get token (authorize) automatically without redirecting? Thank you