SPFx – Using MS Graph API Batch

Introduction

Hi friends, in this post let us see how we can use batching concept in MSGraph API. Let us see what is batching and then will see how we can implement batching. 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 request to fetch different thumbnails. For 10 users and for 3 different thumbnails, you end up in sending 30 different requests to fetch the response and you have to wait for all the response before you continue to process the response. 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 below code fetch the sample users photo of specific dimension passing each query as an individual request.

Note: The code mentioned below use 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 send the individual request for each user. The entire requests is declared inside the Promise which means that the final result should contain the photos of all the users. Below is the output of the above code in the browser console.

Multiple requests has 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 reduce the risk of losing the data, increase the performance and there are much more benefits of using batching.

Now let us see how the same functionality shown above can be used with 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 hard coded 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 user to be pushed to a single batch. This variable is specific to this scenario. Since for 1 user we need to send 3 requests so, the userBatchLimit is set as 6 which means 6 users with 3 requests for each and in total 18 requests. Let us see the details in step by step approach.

  • The sample users list count is verified against userBatchLimit
  • If the count is less than 6, then for all the users we construct the requests and then sending in batch
  • If the count is greater than 6, then the list is split in to 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 users 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

Thats it, try to use the above method. But before using this make sure you have all the permissions setup in the API permission for SPFx.

Happy Coding….

One thought on “SPFx – Using MS Graph API Batch

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s