SPFx - Using MS Graph API Batch

SUDHARSAN KESAVANARAYANANSharePoint Architect
CERTIFIED EXPERT
Microsoft MVP Office Dev focussed on Azure services, M365, SharePoint On-Premise & SharePoint Online, Power Automate, Power Apps, LiveTiles
Published:
Edited by: Andrew Leniart
Using batching to fetch different thumbnail photos for users using MSGraph API.
Introduction

Hi friends, let us see how we can use the batching concept in MSGraph API, what batching is and then how we can implement it. The batching can be implemented in different ways, and this post covers one of the many ways which I thought is simple to follow.

What is batching?

Batching is a concept of grouping multiple requests and then sending the group of requests as a single request to the server. Consider a common scenario where you have to fetch different thumbnail photos of the users. For each user, you could send multiple requests to fetch different thumbnails. For 10 users and 3 different thumbnails, you end up sending 30 different requests to fetch the response and must wait for all the responses before you continue to process them. What if there is an alternate way of reducing the 30 requests to 1 or 2? Batch requests will help in this case.

Focus on the code

First, let us see the code without batch. The code below fetches the sample users photo of a specific dimension, passing each query as an individual request.
Note: The code mentioned below uses React Hooks, so the functions or methods are declared as constants.

const sampleUsers = [    'AdeleV@tenantname.onmicrosoft.com',    'AlexW@tenantname.onmicrosoft.com',    'DiegoS@tenantname.onmicrosoft.com',    'IsaiahL@tenantname.onmicrosoft.com',    'LeeG@tenantname.onmicrosoft.com' ]; 
const getUserPhotos = async () => {        let finalResponse: any[] = [];        Promise.all(sampleUsers.map(async user => {            try {                let response = await props.graphClient.api(`/users/${user}/photos/240x240/$value`).responseType('blob').get();                finalResponse.push(response);            } catch (error) {                console.log(error);            }        })).then(() => {            console.log(finalResponse);        });    }
The method getUserPhotos iterates the sampleUsers and sends individual requests for each user. The entire requests are declared inside the Promise, which means that the final result should contain all the users' photos. Below is the output of the above code in the browser console.


Multiple requests have been sent to the server to fetch the photos for different users. This is not the best practice recommended by Microsoft, but you can use it for minimal requests but not for bulk requests. The best practice is to use the batching technique for the bulk requests, which reduces the risk of losing the data, increases performance and has many more benefits of using batching.

const batchItemLimit: number = 18; const userBatchLimit: number = 6; const sampleUsers = [    'AdeleV@tenantname.onmicrosoft.com',    'AlexW@tenantname.onmicrosoft.com',    'DiegoS@tenantname.onmicrosoft.com',    'IsaiahL@tenantname.onmicrosoft.com',    'LeeG@tenantname.onmicrosoft.com' ]; 
const getUserThumbnailPhotos = async (): Promise<any[]> => {        let finalResponse: any[] = [];        return new Promise(async (res, rej) => {            if (sampleUsers && sampleUsers.length > 0) {                let requests: any[] = [];                if (sampleUsers.length > userBatchLimit) {                    let chunkUserArr: any[] = chunk(sampleUsers, userBatchLimit);                    Promise.all(chunkUserArr.map(async chnkdata => {                        requests = [];                        chnkdata.map((user: any) => {                            requests.push({                                id: `${user}_1`,                                method: 'GET',                                responseType: 'blob',                                headers: { "Content-Type": "image/jpeg" },                                url: `/users/${user}/photos/48x48/$value`                            }, {                                id: `${user}_2`,                                method: 'GET',                                responseType: 'blob',                                headers: { "Content-Type": "image/jpeg" },                                url: `/users/${user}/photos/96x96/$value`                            }, {                                id: `${user}_3`,                                method: 'GET',                                responseType: 'blob',                                headers: { "Content-Type": "image/jpeg" },                                url: `/users/${user}/photos/240x240/$value`                            });                        });                        let photoReq: any = { requests: requests };                        let graphRes: any = await props.graphClient.api('$batch').post(photoReq);                        finalResponse.push(graphRes);                    })).then(() => {                        res(finalResponse);                    });                } else {                    sampleUsers.map((user: any) => {                        requests.push({                            id: `${user}_1`,                            method: 'GET',                            responseType: 'blob',                            headers: { "Content-Type": "image/jpeg" },                            url: `/users/${user}/photos/48x48/$value`                        }, {                            id: `${user}_2`,                            method: 'GET',                            responseType: 'blob',                            headers: { "Content-Type": "image/jpeg" },                            url: `/users/${user}/photos/96x96/$value`                        }, {                            id: `${user}_3`,                            method: 'GET',                            responseType: 'blob',                            headers: { "Content-Type": "image/jpeg" },                            url: `/users/${user}/photos/240x240/$value`                        });                    });                    let photoReq: any = { requests: requests };                    finalResponse.push(await props.graphClient.api('$batch').post(photoReq));                    res(finalResponse);                }            }        });    };
The code compared to the previous is huge, but the logic is simple.
The scenario is to pull 3 different thumbnail photos for the hardcoded users.
The variable batchItemLimit is declared to fix the requests that can be sent per batch.
The variable userBatchLimit is to maintain the number of requests for the user to be pushed to a single batch. This variable is specific to this scenario. For 1 user, we need to send 3 requests, so the userBatchLimit is set as 6, which means 6 users with 3 requests for every 18 requests. Let us see the details in a step by step approach.

  • The sample users list count is verified against userBatchLimit
  • If the count is less than 6, then we construct the requests and then send in batch for all the users.
  • If the count is greater than 6, then the list is split into chunk based on the userBatchLimit, and then the requests are batched and sent to the server.

Below is the output of the batch requests.


Based on the above response, there is only one request sent to the server which has 3 requests for each user coupled and finally we will get a single response with all the user's different thumbnail photos as a blob.

Note: There are some limits on requests count that can be sent in a batch. Batching requests to Microsoft Graph - Code Samples | Microsoft Docs
That's it; try to use the above method. But before using this, make sure you have all the permissions set up in the API permission for SPFx.

Happy Coding...
0
501 Views
SUDHARSAN KESAVANARAYANANSharePoint Architect
CERTIFIED EXPERT
Microsoft MVP Office Dev focussed on Azure services, M365, SharePoint On-Premise & SharePoint Online, Power Automate, Power Apps, LiveTiles

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.