Different ways of consuming organizational data (i.e. SharePoint or MS Graph API or other) from SPFx web parts

In this post, I'm going to cover different options when it comes to accessing organizational data from SPFx web parts. By organizational data I mean the most common APIs like SharePoint or MS Graph. However, it's valid for all other APIs as well. 

I'm going to cover two primary options here. The first one doesn't include any custom services or custom APIs and relies solely on SPFx OOB functionality. In the second option, we use Azure Function as a custom Web API component to access organizational data. As a bonus, we also have the third one which is a combination of the first two.

SharePoint Framework only solution

Authentication schema

How it works?

In order to access any organizational service, our HTTP request needs to be authenticated. Thus, every scenario includes authentication against APIs. In the current scenario, we can access SharePoint data directly, because normally an SPFx web part runs on a SharePoint page, i.e. on the same domain, as our SharePoint API. That's why on the schema above you see "Direct access" for SharePoint API. Technically speaking, it doesn't mean "no authentication", because, under the hood, SPFx sends authentication cookies, attached to your logged-in user. So, authentication works, but we don't need to care about it. 

For MS Graph API situation is a bit different. SPFx uses OAuth implicit flow to generate a bearer access token for MS Graph specifically. Then MS Graph token is attached to every request. In that way is implemented authentication for MS Graph in the first scenario. For authentication, msal.js is used (or will be used in the near future, because it was a plan from Microsoft to migrate from adal.js to msal.js). All authentication staff is hidden internally and basically, you shouldn't care about it in your code. 

Code samples

For accessing SharePoint API we use SPHttpClient. For example, to get information about the SharePoint web:

this.context.spHttpClient
  .get(`${this.context.pageContext.web.absoluteUrl}/_api/web?$select=Title`,
    SPHttpClient.configurations.v1,
    {
      headers: [
        ['accept', 'application/json;odata=nometadata'],
        ['odata-version', '']
      ]
    })
  .then((res: SPHttpClientResponse): Promise<{ Title: string; }> => {
    return res.json();
  })
  .then((web: { Title: string }): void => {
    console.log(web.Title);
  });

For MS Graph we use MSGraphClient. Get information about the current user:

this.context.msGraphClientFactory
      .getClient()
      .then((client: MSGraphClient): void => {
        // get information about the current user from the Microsoft Graph
        client
          .api('/me')
          .get((error, response: any, rawResponse?: any) => {
            // handle the response
        });
      });

Read more

Connect to SharePoint APIs

Consume the Microsoft Graph in the SharePoint Framework

Use the MSGraphClient to connect to Microsoft Graph

SharePoint Framework with Azure Functions 

In this solution, we use Azure Function to access organizational data. In other words, we call protected Azure Function endpoint, which in turn, goes to organizational data APIs. 

NOTE. You're not limited to Azure Function, you can use any other web-based API and hosting capabilities, i.e. Azure Web apps, etc.

 Authentication schema

How it works

In this scenario, the SharePoint Framework web part doesn't access SharePoint or MS Graph API directly. Instead, we talk to those APIs using backend code in Azure Function (might be any other web tech). Our Azure function is protected with Azure AD authentication, thus the web part uses OAuth implicit flow to get access token for Azure Function specifically. In Azure Function, we again talk to Azure AD and perform on-behalf-of flow in order to exchange Function access token for a token for another service (SharePoint, MS Graph, etc.). Using on-behalf-of flow we generate a user access token for organizational APIs and act on behalf of a currently logged in user. 

When you have an access token, you can talk to organizational services. You can use either raw HTTP requests or any of the .net libraries. For SharePoint, it might be SharePoint CSOM, PnP Site Core (RETIRED), or PnP Core SDK (the future iteration of PnP Sites Core).

Code samples

In this code sample, I use PnP Core SDK to perform authentication and further call SharePoint API:

var onBehalfAuthProvider = new OnBehalfOfAuthenticationProvider(_appInfo.ClientId, tenantId, clientSecret, () => request.Headers.Authorization.Parameter);
var results = new List<ListData>();
using (var pnpContext = await _pnpContextFactory.CreateAsync(new System.Uri(siteUrl), onBehalfAuthProvider))
{
	var lists = pnpContext.Web.Lists.Load(l => l.Id, l => l.Title).ToList();

	foreach (var list in lists)
	{
		results.Add(new ListData { Title = list.Title });
	}
}

The below code uses MS Graph.NET library to download email and save it to OneDrive:

var graphClient = CreateGraphClient(tenantId, accessToken);
var mail = await graphClient.Me.Messages[mailId].Request().GetAsync();
var mailStream = await graphClient.Me.Messages[mailId].Content.Request().GetAsync();

// upload to root OneDrive folder
await graphClient.Me.Drive.Root.ItemWithPath(mail.Subject + ".eml").Content.Request().PutAsync<DriveItem>(mailStream);

Read more

Connect to Azure AD-secured APIs in SharePoint Framework solutions

Consume enterprise APIs secured with Azure AD in SharePoint Framework

How to access SharePoint data from Azure Function with SPFx and PnP Core SDK

Which one to use?

All options have their own pros and cons. The answer is usually "it depends". However, let's try to figure out potential advantages and drawbacks in the mentioned ways to consume data. 

When it comes to the first one:

+ it doesn't require any backend, thus easier to configure and deploy and maintain

+ requires only one language - Javascript / TypeScript

However:

- all permissions for all APIs should be listed in the package-solution.json file. If you want to add new permission - you should update the manifest and approve newly added permissions

- since you're limited to JavaScript only, you cannot perform some backend only operations, like access databases, heavy data processing, file transformations, mail sending, etc.

If we talk about the second option:

+ backend gives you a lot more power when it comes to different server features you may need - data processing, databases, mail sending, etc.

+ You don't have to update permissions for your web part, because all permissions are maintained in a separate app registration. No need for redeployments when you introduce new permissions in your app. 

However:

- more complicated solution, since requires backend, deployments, .NET or other language knowledge

- it might be a bit slower, when it comes to performance, to access some organizational data since in that case you need one "extra hop" SPFx -> Az Func -> Org API.

So the answer really depends on your requirements. If you're building small SharePoint Framework web parts, if you're absolutely sure, that you don't need any backend now and in the future, if all of your data is inside SharePoint / MS Graph - use the first option. For medium or big solutions, it's a high chance, that you will need some backend processing. Or if you're requirements contain databases, custom data sources, other third-party APIs, heavy processing, etc. Or if you just feel more comfortable with .NET object model. In that case, use the second option to access your organizational data. 

 Combination of the first two

Nothing prevents you from combining all of the mentioned options. You're absolutely not limited here - you can use SPFx JavaScript code to access SharePoint, MS Graph API. At the same time use Azure Function to process data or doing some other server related things. Alternatively, you can access organizational APIs simultaneously from both SPFx side and Azure Function, depending on your needs. 

Simply carefully analyze all requirements (including ones, which might be added in the future), know all options, select the one, which suits you best. 

Title image credits - FreeVector.com