Practical guide to use  the Microsoft Graph-API
Tags: typescript, azure, node, graphapi
Published: 2021-04-06 15:38:45

The Microsoft Graph-API is the gateway to almost all data living within a Microsoft365 subscription, and is therefore a super powerful API for a lot of different use cases.

But approaching and using it for the first time can be very difficult and overwhelming – especially because the [official documentation](https://docs.microsoft.com/en-us/graph/overview) is very complex and information is shattered across a lot of different pages.

I just had to create a serverless function in Node.js that retrieves some data from a SharePoint-List – and while trying to find out how to do that, I got lost clicking from page to page, concept to concept and code example to code example to find a quick and easy solution I can built upon.

## The goal of this post: A pracitcal guide

The main problem I had was: **How to correctly authenticate and authorize my backend-app with a simple secret to use the Graph-API to query general (non-user related) data?**

With this post I want to save you the trouble I went through and show you exactly this in a **practical guide**. I will only explain enough theory for you to understand what is going on.

The guide explains how to set everything up to query the **SharePoint root site** as an example, but it also explains how to adjust everything to match your desired data.

The code examples are written in **TypeScript** and run in **Node.js**. But as we will be using the official Azure auth library [@azure/msal-node](https://www.npmjs.com/package/@azure/msal-node) which is available in other languages as well (you can find a list [here](https://docs.microsoft.com/en-us/graph/auth/)), the concepts should still be applicable if you fancy another language.

I am also planning to write a **second part** to this post that takes a deeper dive into the concepts behind the authentication and authorization models of Microsoft365 and that will explain some leftover questions from this first part – so stay tuned.

But for now, let's get started.

# Prerequisites – What you need to know first

## Azure AD Application required

In order to authenticate with the Graph-API, you must register an app in the **Azure Active Directory** (Azure AD for short). This is the way of creating a technical user for the API, and there really is no way around this.

In hindsight, this was one of the changes in my mental model I had to go through to understand the auth-mechanism of Microsoft365: Rather than creating **a (somewhat anonymous) technical user** to use the API, you actually **register the app itself** as identity within your account.

For the use-case of this post, there is no real technical difference between the two. However, Microsoft allows for far more complex authentication scenarios where this becomes very relevant. I'll talk about those in the second part.

## Administrator Privileges required

You will need administrator privileges to register the app. If you don't have them, there are three other options:

1. You ask your IT-Department or whoever is in charge to register the app for you.
2. You use the [App registrations page](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) in Azure if you have access. This is the admin-free version of registering an app and you can then follow all of the steps below – however, you won't be able to give the consent required in step 1.3. and then still need an administrator to consent for you.
3. You create a **completely free** [Microsoft365 Developer test account here](https://developer.microsoft.com/en-us/microsoft-365/dev-program) – which gives you a full Microsoft365 E5 Subscription for a renewable 90 days. This is perfect as a playground and what I did to test the code of this post.

# Practical Guide

Now lets dive into the practical part which consists of three parts:

- [1. Registering an Azure AD Application](#1-registering-an-azure-ad-application)
- [2. Using TypeScript to query the API](#2-using-typescript-to-query-the-api)
- [3. Using the Graph-Explorer to find the right query](#3-using-the-graph-explorer-to-find-the-right-query)

## 1. Registering an Azure AD Application

If you have administrator privileges, you can reach the Azure AD App registrations page by opening your browser and navigating to <https://aad.portal.azure.com>. This should bring you to the Azure Active Directory admin center of your Microsoft365 or Azure Subscription  once you logged in with your administrator account.

Alternatively, you can reach it from either within your [Microsoft365 admin center](https://admin.microsoft.com/#/homepage) in the navigation on the left, or from the [Azure portal](https://portal.azure.com) by searching for it in the top bar.

Whatever way you chose, once you are there, on the left side click **Azure Active Directory** and then **App registrations**.

![Screenshot of the Azure AD admin center with red circles on Azure Active Directory Link and App registrations and an arrow linking both circles](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g18nopjv0hr686uvq5wo.png)

Next, we'll have to do 3 things: **register the app**, then **create a client secret** and in the end **add some permissions** to it – which I'll explain step by step.

**Don't worry of making any mistakes here – you can always adjust the settings later and there is absolutely no risk of creating any costs as registering an app is completely free**.

### 1.1. Registering the Application

1. Click **New Registration** on the top bar.
![Screenshot of the App registration page with a circle on the new registration button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9t0ngs511ixzmon1uz0x.png)

2. Fill the Form with a **name** of your choosing and select "**Accounts in this organizational directory only**" (you can omit the Redirect URI), then hit `Register`.
![Screenshot of the app registration form, the name field is MyTestApplication and the radio button selects Accounts in this organizational directory only](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zc8stzgezox39n67s8el.png)

You should see a summary screen of your app from which you will later need the values of the **Application (client) id** and the **Directory (tenant) id**. You can copy and store them now or retrieve them again later.

Think of those variables like this:

- **tenant-id**: The unique Id of the entire Microsoft365- or Azure-Account.
- **client-id**: The username of the technical user our application will use to authenticate.

Now all that is missing for a proper **authentication** is a password or `client-secret` – we will create this in the next step.

### 1.2. Authentication: Create a client-secret

1. On the left, select `Certificates & secrets` and click `New Client secret`.
![Screenshot of the App registration page with red circles around Certificate & secrets and New client secret and an arrow connecting both](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ueivczrzozc3y22d3kf.png)

2. Give it any name and choose an expiry date (you can just use `Never` for now).
![Screenshot of the Add a client secret form with the Description input set to TestSecret and the Expires radiobutton selecting Never](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhnkwat1015mt6dm028y.png)

3. You should see the created secret and the value in clear text. **Copy and store this value somewhere now – this will be the only time you can see it.**
![105_Store_Value](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aylac2vwj5xwnqo6r6lm.png)

One note: Don't confuse the `ID` field of this screen with the **client-id** of the previous step – they are not the same and you won't need the **client-secret-id** from here anymore.

Now we have everything we need to let our application **authenticate** against Azure AD. However, we didn't tell Azure-AD what data our application is allowed or **authorized** to retrieve yet, so lets do this next.

### 1.3. Authorization: Grant permissions to your app

Still on the Azure AD Application page:

1. Select **API Permissions** and hit **Add Permissions**.
![Screenshot of the app registration page with red circles on the API permissions navigation and the add permissions button and an arrow connecting the two](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xmu0atwak5roy5oljr3x.png)

2. Select **Graph API**.
![Screenshot of the  Request API permissions form showing the selection of the microsoft graph api](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0balmq2dvgvrtrme44s3.png)

3. Select **Application permissions**.
![Screenshot of the Request API permissions form with a red circle around the Application permissions button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gryf1nunpm63w7plu6ga.png)

4. Search and select **Sites.ReadAll**. This is the permission that allows us to read all SharePoint-Data. If you need to query other data and therefore need other permissions, you can later use the [Graph-Explorer as explained in Step 3](#3-using-the-graph-explorer-to-find-the-right-query).
![Screenshot of the Requst API permissions form where the search input is Sites and the selected permission is Sites.Read.All](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dnq5048b10mozq58b9pw.png)

5. Click **Add Permissions**.

6. Back on the permissions-screen, click **Grant admin consent** to actually allow those permissions. I'll explain in the second part why this is necessary, for now just do it.
![Screenshot of the API permissions overview with a circle on the grant admin consent button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xckddurxf8jaw27ambwa.png)

And that's it. Now we are ready to write some code to use the Graph-API and retrieve SharePoint-Data from our Microsoft365 account.

## 2. Using TypeScript to query the API

### 2.1. Install the necessary libraries

```bash
$ npm install @azure/msal-node node-fetch
```

MSAL stands for *Microsoft Authentication Library* and
[@azure/msal-node](https://www.npmjs.com/package/@azure/msal-node) is the official library to authenticate with Azure AD from a Node.js Application. It kind of suffers from the same bloated and confusing documentation as the Graph-API, so finding the right classes and functions to use can be very cumbersome. However, we will see in the code below that we really only need a few lines to make it work.

We are also installing [node-fetch](https://www.npmjs.com/package/node-fetch) as the Graph-API is an http-endpoint – but you can use any other http-library you fancy.

If you found the official client [@microsoft/microsoft-graph-client](https://www.npmjs.com/package/@microsoft/microsoft-graph-client) on npm – do not use it. At least according to their documentation, it is not compatible with the simple credential-authentication that we are trying to use here.

### 2.2. Create a file `queryGraphAPI.ts`

Have a look at the full code-example first and explain the details after:

```javascript
import * as msal from '@azure/msal-node';
import fetch from 'node-fetch';

// 1.
const tenantId = '<YOUR_TENANT_ID>';
const clientId = '<YOUR_APPLICATION_ID>';
// 2.
const clientSecret = '<YOUR_CLIENT_SECRET>';
// const clientSecret = process.env.CLIENT_SECRET


const clientConfig = {
  auth: {
    clientId,
    clientSecret,
    // 3.
    authority: `https://login.microsoftonline.com/${tenantId}`
  }
};

// 4.
const authClient = new msal.ConfidentialClientApplication(clientConfig);

const queryGraphApi = async (path) => {
  // 5.
  const tokens = await authClient.acquireTokenByClientCredential({
    // 6.
    scopes: ['https://graph.microsoft.com/.default']
  });

  const rawResult = await fetch(`https://graph.microsoft.com/v1.0${path}`, {
    headers: {
      // 7.
      'Authorization': `Bearer ${tokens.accessToken}`
    }
  });
  return await rawResult.json();
}

export {
  queryGraphApi
};

```

#### How this code works

1. You will recognize the **tenantId**, **clientId** from step 1.1. – fill them in here directly.

2. The **clientSecret** from step 1.2. is **sensitive information**, so you should not be using it in your code and **never commit it to your repo**. For a quick test it's okay, later on you better provide this value through an environment variable.

3. The authority is the endpoint that the msal-library will authenticate with. Maybe now it is clear why you need the unique **tenantId** – it let's the generic login-endpoint of microsoft know for which account your are trying to authenticate.

4. We are using the `ConfidentialClientApplicaton`-Class of `msal`. It is named confidential as our application is running completely in the backend where the `clientSecret` is safe. There is a differentiation to `public` clients as the `msal` library also offers authentication mechanisms for browser-based applications where using a general secret would not be secure (everyone could read and use it).

5. As you might see, we do not query the Graph-API with our credentials directly, but we only use them to get an **access-token**. If you are familiar with the OAuth2 and OpenID stack, you might recognize this pattern. If not don't worry, I'll talk more about it in the second part.

6. With the **scope**, we tell the auth-endpoint that we want the token to be allowed to access the Graph-API with the `.default`-permissions – which are the ones we configured already in Step  1.2. For our use-case, this is the only possible option, but again there are other use-cases where setting other values here makes sense (which – you guessed it – will be covered in the second part).

7. Finally, we query the Graph-API endpoint with the retrieved token. The `path` Parameter defines which data to query, and in 2.3. we will use it with `/sites/root` which is the SharePoint Endpoint.

### 2.3. Use the API in an `index.ts`

```typescript
import { queryGraphApi } from './queryGraphAPI.ts'
// In case you don't have top level await yet
async function start() {
  const sharePointSite = await queryGraphApi('/sites/root');
  console.log(sharePointSite);
}

start().then(() => console.log('Complete.'));
```

Now if you start the program you should see a result like this:

```shell
$ npx ts-node index.ts
{
  '@odata.context': 'https://graph.microsoft.com/v1.0/$metadata#sites/$entity',
  createdDateTime: '2021-03-13T12:54:48.047Z',
  description: '',
  id: 'davelosert.sharepoint.com,0d19a070-63ba-40f8-872a-f83df5120b2a,770d1b68-3e43-44f2-aab4-ffc0bfe8c6a1',
  lastModifiedDateTime: '2021-04-02T15:21:33Z',
  name: '',
  webUrl: 'https://davelosert.sharepoint.com',
  displayName: 'Communication site',
  root: {},
  siteCollection: { hostname: 'davelosert.sharepoint.com' }
}
Complete.
```

Of course, this is just really simple code for a first working solution, but it's enough if you just need to query your data once in a while. If you plan to query the api more often, you might consider caching the token rather than requesting it on every query. The msal-library already supports caching
by providing a plugin and you can find an example [on the Github Documentation](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/configuration.md) – but covering this is out of scope of this post.

## 3. Using the Graph-Explorer to find the right query

Okay so now you should be able to query the SharePoint-Root-Site. However, I am pretty sure this is not the data you are after. So where to go from here?

One option is to start looking in the [Graph-API reference documentation](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0) to get an overview of what is possible.

A more interactive and my recommended approach is using the [official Graph-Explorer](https://developer.microsoft.com/en-us/graph/graph-explorer). This browser-based application lets you play around with and query the Graph-API and get immediate feedback about the responses. This makes it a great tool to find out both, the **exact path and query** as well as the **permissions** you need for your use-case.

![Screenshot of the Graph-Explorer with an executed search where the Modify permissions tab is selected](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jfuoqyky9ejd64qvutpa.png)

The Graph-Explorer is mostly self-explanatory, so I won't get into too much detail here. But an approach to find the right query might be:

1. On the left, select a **Sample Query** that comes closest to what you are after
2. Run the query.
3. You might need to give consent in the tab **Modify permissions** so the Graph-Explorer is allowed to query the data in your name.

    *Do you recognize this pattern? This is bascially the same as the admin-consent we gave in 1.3. – but this time for the Graph-Explorer App and rather than in the name of an admin, you give it in the name of **your** account.*

4. The **Modify permissions** tab is also a great place to see which permissions are required to query the endpoint. However, it sometimes shows more permissions than you need.

    For example for the sample query *SharePoint Sites / my organization's default SharePoint Site* (which is our query from Step 2), it shows both,
**Sites.Read.All** and **Sites.ReadWrite.All**. But as we saw, the former is enough to read and the latter is only required if you also plan to write to SharePoint.

    Once you know which permissions you need, you can add them in the App registrations page like we did in [Step 1.3](#13-authorization-grant-permissions-to-your-app).

5. Refine the Query until the output matches what you are looking for.

**One last thing to consider:**
As described, the Graph-Explorer runs in the context of your logged-in user, not your application. This means even if you **consent** here, your App will not automatically get those permissions. As explained in 4., you still have to explicitly add them to your app.

# Summary

As you might have seen, getting started with the Graph-API can be quite challenging. Especially the authentication and authorization part are a bit more complex to set up and not very intuitive at first.

But once you know what to do and where to look, it is only some clicks and a few lines of code to make it work. For the serverless-function described in the intro, it took me hours to make it all work. But registering the application and setting up the code examples for this post only took me around 15 minutes.

**So hopefully I reached my goal to save you the hours of work and get started with the Graph-API more quickly.**

As announced, I will soon publish a second blog-post where I'll go a bit deeper into the theory and concepts behind all of it.

However, if you have any feedback or open questions, feel free to comment below and I'll answer as soon as possible.