From 0 to 60 or how I refactored an Office Add-in from a web page hosted in SharePoint to an application using Node.js, CORS and Office 365 API
I've been working with the Office Graph for a while now. One of the early days I got the idea of building an Office Add-in that would help you get content for the document that you are editing. Often I'm working on proposals or technical designs. These documents contain certain text fragments, such as introduction to our company and our services, which are specific for the particular type of document. I was confident that the Office Graph would help me find similar documents to the one I was working with.
The idea that I had was to build an add-in in the Office clients that you could open, enter a search phrase and have it query the Office Graph for documents containing that phrase. Documents that you have edited more recently would be scored higher than documents that you have just viewed in the past.
Back when I got that idea, we called Office Add-ins Apps for Office and Office 365 API was just released. And while I had built a few Office Graph queries before, there was no way to build an Office Add-in that would connect to the Office Graph - at least none that I knew of. And so I started building a Single Page Application hosted in a web page stored in a Document Library in SharePoint.
Building a SPA as a web page stored in SharePoint wasn't that much of a bad idea back then. You didn't have to think about the hosting, deployment, security, authentication or cross-domain calls. The SPA would just work because all it was, was a web page displayed in an iframe in an Office client. The only problem was that you had to sign in to Office 365 in Internet Explorer first for it to work. Other than that I was able to query the Office Graph directly from the Office client of my choice.
Eventually I built a web page using jQuery for AJAX calls to the Office Graph and KnockoutJS for data binding in the UI. Things were okay.
At some point the Office 365 API evolved and Azure started supporting registering applications with Search permissions. That was the moment when I decided to refactor the add-in and do things better.
As the Office 365 API evolved more and more samples have been released to dev.office.com and the Office 365 GitHub repo. These samples showed you how to build Office Add-ins connected to Office 365. The only problem that I had was the fact, that the Office 365 SDK for .NET didn't provide any entry point to connect to the Office Graph. And so the add-in remained a web page hosted in SharePoint.
When I started working with Node.js there was no Office 365 SDK for it. This is still the case today when I'm writing this post. If you wanted to connect to Office 365 you had to do it yourself. But before you could do that, you had to authenticate with Azure AD. To figure it out I had to learn how OAuth works and what the different flows are. Even though SDKs will make authentication easier in the future, knowing how things work behind the scenes is invaluable.
And so using Node.js I was able to build a server-side wrapper for authenticating with Azure AD and calling the Office Graph. From the KnockoutJS spa I would then communicate with my API wrapper to get the data from the Office Graph. Things were looking promising.
Recently I've learned that some Office 365 APIs support CORS. One of these APIs is the SharePoint API which is currently used to communicate with the Office Graph. The great thing about CORS is that it allows your application to call the API on another server directly from the client eliminating the need for server-side wrappers to facilitate cross-domain calls!
Unfortunately right after authenticating with Azure AD I found out that the Discovery Service doesn't support CORS. You need the Discovery Service whenever you're building multi-tenant applications and need to get the URL of the SharePoint online tenant of the current user.
Having done some work with Node.js, building a server-side wrapper to call the Discovery Service wasn't much of a problem. The thing that was a problem, was how to authenticate with it.
Whenever you're calling the Discovery Service you have to send a valid access token along with your request. You can get one by following the token flow but that involves redirects to the Azure AD login page - something you cannot do when calling an API. An alternative would be to exchange the refresh token for an access token to the Discovery Service but when using CORS all you get is an access token.
Eventually I've found out about yet another OAuth flow in Azure AD that allows you to do just that: exchange one access token for another. There are some restrictions with regards to the access token that you want to exchange but for getting from CORS to the Discovery Service things just work.
And so I had a multi-tenant Office Add-in querying the Office Graph working. Things were cool.
The add-in worked but there were still a thing or two I wanted to research. One thing I wondered about in particular was how my application would look like if I used TypeScript.
I started refactoring the application bit by bit. At the end I refactored both the Node.js server-side wrapper for calling the Discovery Service and the KnockoutJS View Model containing the SPA logic to TypeScript. The application I was working on wasn't complex at all so it turned out to be a good place to start working with TypeScript.
When working with TypeScript what I did find difficult was dealing with external libraries that don't have definition files available for them yet (like ADAL JS or Office). Sure you could just set those objects to
any, but what's the use of TypeScript if you do that in the first place? I ended up building the definition files myself - at least for the parts used by my application. It took some time to figure it out, but eventually I had intellisense and validation for all objects used by my application.
Another thing that took me some time to figure out was how TypeScript deals with the
this references. In promises or callbacks,
Currently the Office Add-in is almost perfect. There is however still one last thing I want to refactor in the future: leveraging Office 365 unified API.
The Office 365 unified API is the future of how applications will be able to communicate with Office 365. All the different services available as a part of Office 365 will be available from a single URL and the API will support CORS. Although the preview release of unified API is already available, it currently doesn't support the Office Graph. But I am looking forward to the day when it will and we will be able to build rich add-ins and applications 100% using client-side development techniques.
Looking back at the whole process: from the moment I got the idea to now, it's been a great experience! I learned a lot experimenting with the different techniques and tools. Particularly after years of developing SharePoint solutions, it's been impressive to see what else is possible beyond WSPs and Feature XML.
Even if you currently don't have projects involving working with the latest Office 365 APIs and web techniques, I would strongly recommend that you think of a pet project and start building it using something you haven't worked before with like I did with Node.js. There are tons of resources available on the Internet to help you started. The process won't be easy and you might consider giving up a few times along the road, as Chris Johnson mentioned in the latest Microsoft Cloud Show podcast, but it's absolutely worth trying.
The process I described here took me a couple of months. I haven't worked full time on my application and sometimes had to wait on things to become possible in the first place to be able to continue (like Search permissions in Azure). If all you know is SharePoint, I can tell you that there is a lot more out there. The good news is, that once you get started, things will get easier and you will start seeing new possibilities all around you.
There are still many things that I want to try and I realize how little I worked with web development when doing SharePoint projects. I'm happy I took the leap and I'm grateful for everyone who helped me out along the way. There are many great folks out there willing to help you but it's you who has to take the first step. And I hope you do, because there is a whole world beyond WSPs.