SharePoint on the Graph
In the past few months whenever I review the REST API documentation for SharePoint, there is always this menu item on the left-hand column
This will inevitably take me through to the MSGraph — and with that, there are new libraries and new ways to sorting things out. And of course, most tutorials online take things to the ‘Hello World’ stage. For this I want to take from nil to a small node app that will:
- Access a SharePoint site
- Create a Folder in a Document Library
- Create a Document in a Document Library
- Add an entry to a list
Hopefully, once we are done we have an approach that you can take to creating a console app that will connect to the MS Graph.
Setup Application
There are going to be a couple of key links that are going to help you on your journey here:
- Microsoft Graph Documentation https://docs.microsoft.com/en-us/graph/
- And the Graph Explorer https://developer.microsoft.com/en-us/graph/graph-explorer
These will give you the basis for what calls are possible and through the use of the graph explorer the confidence to test the calls to the graph.
Now at the end of this registration process, we are going to need the following outputs:
- a client id
- an authority URL which is made of up https://login.microsoftonline.com/ and your tenant id
- a client secret
- the graph endpoint (which for most people will just be https://graph.microsoft.com/)
Now before we get too deep, Microsoft has done a great job document a lot of what we will need to do to register a new application.
So let's start, browse to your Azure Active Directory (portal.azure.com), and then select App Registrations and we will register our new app.
Given the calls we making (to specific SharePoint sites), we will need to ensure that we only select a single tenant in our app registration.
With the app registered we can collect the client id and the tenant id.
Next, we need to generate our application secret, on the left-hand panel select Certificates & secrets and then click new client secret
With that new client secret generated we have what we need to start developing our app.
App Permissions
However, before jumping into the code right away we want to take a beat and look at what permissions we are going to need. Using the graph explorer can test the API calls that you can make again the MS Graph service. Using the default calls (Files and SharePoint) you can use the Modify Permissions tab to get a read of the permissions that you are using
For this project it will be:
- Files.ReadWrite.All
- Sites.Read.All
- Sites.ReadWrite.All
This might be overkill for many situations and you are likely to need to tune this up and down.
Now that we have the permissions we need, we can add Files.ReadWrite.All
Sites.Read.All
and Sites.ReadWrite.All
permissions to the application.
After you add your permissions you can then grant Admin consent to the permissions.
Setup SharePoint
There is not much point to all of this if we don't have a SharePoint to point at. For my example, I have set up a simple communication site with a Document Library
In addition, I have set up a basic list that we can query
With that setup, let's proceed.
Let's write some Code
For our node project, we are going to create a console-based app that will have a menu to enable us to select the various functions we want to execute.
To connect to the MS Graph we will use the @azure/msal-node
module to authenticate, and we will use that authentication method to plug into the graph client @microsoft/microsoft-graph-client
, and using that graph client we are able to access the data within SharePoint.
Setup the Project
Setup your node project (do the npm init dance) and then we will install our dependencies:
npm install @azure/msal-node @microsoft/microsoft-graph-client inquirer uuid --save
Next, install the dev dependencies
npm install typescript tslint ts-node @types/inquirer @microsoft/microsoft-graph-types @types/uuid --save-dev
Setup TypeScript
Next, we set up TypeScript — now there are a bunch of ways to set this up — but for me, I tend to use the same as the previous projects however you can get away by simply using
tslint --init
This will create a tslint.json at the root of your project. Finally, we will add a tsconfig.json to the root of this project
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "esnext",
"noImplicitAny": true,
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist",
"baseUrl": ".",
"paths": {
"*": [
"node_modules/*"
]
},
"emitDecoratorMetadata": true,
"experimentalDecorators": true
},
"include": [
"src/**/*"
]
}
Update package.json
To complete the setup we want to add some additional scripts to the package.json
"prebuild": "tslint -c tslint.json -p tsconfig.json --fix",
"build": "tsc",
"dev": "nodemon --watch './src/**/*.ts' --exec ts-node ./src/index.ts",
"rundev": "ts-node ./src/index.ts"
Folder Structure
The folder structure that we will be using for this is very simple.
There are a couple of things to point out:
- The temp folder will be used to store documents that we will use to upload documents
- and the src/environments folder will be used to store my environment variables
Custom Authentication Provider
To authenticate with the Microsoft graph in node we will use the @azure/msal-node
module and the AuthenticationProvider
class from the @microsoft/microsoft-graph-client
module.
To start I define an interface that will contain our four key outputs from our application registration:
- Client Id
- Client Secret
- Authority Endpoint
- And Graph Endpoint
As we are creating our own custom authentication provider we will extend the existing AuthenticationProvider
class. This will have two methods:
- a
contstructor
method that will take the config as input and set up the default scopes - and a
getAccessToken
method which will return an access token
Graph Client
Now that we have our authentication provider in place, let's plumb it into our graph client. This is a new class that we will use in our main app. For now we will only implement the following methods:
- get — for GET requests
- post — for POST requests
- put — for PUT requests
The main application
For our main application, we will create a main function that will prompt the user to make a selection. In addition, we will create separate functions that will provide the functionality for the following SharePoint actions:
- Access SharePoint site information
- Access SharePoint list
- Add an Item to a SharePoint list
- Create a folder in a Document Library
- Upload a document
Next, you can run npm run dev
and you should now be presented with a list of menu items.
SharePoint Site Info
This is a nice gentle introduction. This will become the basis of a lot of future calls as it will give us the correct ids to use when traversing the MS Graph
Running the program and selecting the Access SharePoint Information option should return the following:
{
'@odata.context': 'https://graph.microsoft.com/v1.0/$metadata#sites/$entity',
createdDateTime: '2021-02-10T03:10:03.59Z',
description: 'SampleSharePoint',
id: 'sample.sharepoint.com,42b6123-1234-1234-b1d2-d0f912c2ed24,3f12c123-bd12-12a3-12fe-123d1c123d12',
lastModifiedDateTime: '2021-03-29T23:01:55Z',
name: 'PigglesSharePoint',
webUrl: 'https://sample.sharepoint.com/sites/SampleSharePoint',
displayName: 'SampleSharePoint',
root: {},
siteCollection: { hostname: 'sample.sharepoint.com' }
}
Access SharePoint list
Next, we build upon that and we access a SharePoint list. This time we use the site id got by using: await graph.get(sharePointUrl);
Using that id, we can then get back the array of all the lists (which is returned as an array). We loop through that array to return the list information.
Add Item to SharePoint list
Now that we have the List Id we can expand this to add a new list item to the SharePoint list.
The format for the body of this request is simple:
const listItem = {
fields: {
Title: 'SharePoint using Graph',
Value: uuidv4()
}
};
For the title we are using the uuid library to add some uniquness to the entry.
And when we run the app again get select Add item to SharePoint list we get our new list item 🥰
Create Folder in Document Library
Now we are on to the Document Libary parge of the API. Now this is all based on the OneDrive API. But instead of using a url like this:
https://graph.microsoft.com/v1.0/me/drive/root/children
we replace the ‘me’ in the url with the SharePoint site it.
https://graph.microsoft.com/v1.0/sites/sample.sharepoint.com,siteid,documentlibraryid/drive/root/children
To add a new folder, the body will be simple and as with the previous example we are using uuid to create a unique folder.
const foldername = {
"name": uuidv4(),
"folder": {}
};
Below is the function
Now we are run that again and this time we can see a folder created successfully in the document library.
Upload a Document to Document Library
The approach that you take to uploading documents using the MS Graph API will greatly depend on the size of the files. For documents that are smaller than 4 MB, you can use a standard put. For larger documents, you will need to create an upload session, and in that session, you can only upload 60 MB chunks at a time.
Happily, the MS Graph client allows some additional methods that will help us out.
First of all, let's sort out the small/large file problem — for this we get the stats from the file we are uploading
const fSize = statSync(uploadFile);
Then you can check the file size and create your put statement:
Now for the large file we will need to two new methods to our MS Graph Client:
pathToFile — this will take a filepath (String), and filename (String) as inputs and return a File object. The methods that we will be using will need this to ensure that the larger file gets chunked correctly.
addLargeFile — this method will take the URL, filename, file size, and file object and use the LargeFileUploadTask to create the session and upload the file to SharePoint
With that in place, we can complete our else statement (i.e. handle files over 4 Mb in size).
const largeFileUploadSessionUrl = `/sites/${getSiteId.id}/drive/items/${getRootId.id}:/${fileName}:/createUploadSession`const largeFile = graph.pathToFile(uploadFile, fileName);
const largeFileUpload = await graph.addLargeFile(largeFileUploadSessionUrl, fileName, fSize.size, largeFile);
Below is the complete function
And finally our updated Client
And what is success without a screenshot 😍😍
Wrapping up
In summing up the biggest challenges here was getting the initial authentication behaving constantly, and then after that getting the document upload working (especially with larger files).
Hopefully, this helps the next person who needs one place to run through everything.
Source code for the complete project is below — have fun 😁