Introduction
In this article, we will learn about making SharePoint Online Batch Request Call with a few lines of code using Batch Utils which I have created.
Description
For easy SharePoint List Item CRUD Operations, I have created an SPRest utility. You can find a detailed article on how to use SPRest in development over
here.
If you are working with SharePoint Online, then you definitely have used SharePoint REST API Call to get data with multiple requests from the same site collection.
So, as per the client requirement, you may have to request 5, 10, 15, or any number of AJAX requests to fulfill the requirement. By doing so, you may be stuck with a performance issue for a large number of requests.
If you are using SharePoint Online, SP 2016, or SP 2019 then there is good news. Microsoft has introduced OData $batch API support for REST Call.
Using $batch REST API Call, you can make up to 100 HTTP Requests with Single Batch Request.
There are limited articles and blogs which describe how to use $batch API in SharePoint Online. I have found difficulty in configuring or writing $batch REST Call.
For developers, I have created this SharePoint Batch Utility which is easy to integrate and easy to use for the Get operation. The code is properly exaplined via commented lines.
BatchUtils.ts
-
-
-
- var BatchUtils = (() => {
-
-
-
-
-
- function buildBatchRequestBody(endPointsToGet, boundryString) {
- var propData = new Array();
- for (var i = 0; i < endPointsToGet.length; i++) {
- var getPropRESTUrl = endPointsToGet[i];
- propData.push('--batch_' + boundryString);
- propData.push('Content-Type: application/http');
- propData.push('Content-Transfer-Encoding: binary');
- propData.push('');
- propData.push('GET ' + getPropRESTUrl + ' HTTP/1.1');
- propData.push('Accept: application/json;odata=verbose');
- propData.push('');
- }
- return propData.join('\r\n');
- }
- function BuildChangeSetRequestBody(changeSetId, action, endpoint, items) {
- var batchContents = [];
- let item;
-
- for (let i = 0; i < items.length; i++) {
- item = items[i].data;
-
-
- endpoint = items[i].reqUrl;
- batchContents.push("--changeset_" + changeSetId);
- batchContents.push("Content-Type: application/http");
- batchContents.push("Content-Transfer-Encoding: binary");
- batchContents.push("");
- if (action === "UPDATE") {
- batchContents.push("PATCH " + endpoint + " HTTP/1.1");
- batchContents.push("If-Match: *");
- batchContents.push("Content-Type: application/json;odata=verbose");
- batchContents.push("");
- batchContents.push(JSON.stringify(item));
- } else if (action === "ADD") {
- batchContents.push("POST " + endpoint + " HTTP/1.1");
-
- batchContents.push('Accept: application/json;odata=verbose');
- batchContents.push("Content-Type: application/json;odata=verbose");
- batchContents.push("");
- batchContents.push(JSON.stringify(item));
- } else if (action === "DELETE") {
- batchContents.push("DELETE " + endpoint + " HTTP/1.1");
- batchContents.push("If-Match: *");
- }
-
-
-
-
-
- batchContents.push("");
-
-
-
-
- }
-
- batchContents.push("--changeset_" + changeSetId + "--");
-
-
- return batchContents.join("\r\n");
-
- }
- let BuildChangeSetRequestHeader = (batchGuid, changeSetId, batchBody) => {
- let batchContents = [];
-
-
- batchContents.push("--batch_" + batchGuid);
- batchContents.push(
- 'Content-Type: multipart/mixed; boundary="changeset_' +
- changeSetId +
- '"'
- );
- batchContents.push("Content-Length: " + batchBody.length);
- batchContents.push("Content-Transfer-Encoding: binary");
- batchContents.push("");
- batchContents.push(batchBody);
- batchContents.push("");
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- batchContents.push("--batch_" + batchGuid + "--");
-
- return batchContents.join("\r\n");
- }
-
-
-
-
-
-
- function buildBatchRequestHeader(userPropsBatchBody, boundryString) {
- var headerData = [];
- headerData.push('Content-Type: multipart/mixed; boundary="batch__' + boundryString + '"');
- headerData.push('Content-Length: ' + userPropsBatchBody.length);
- headerData.push('Content-Transfer-Encoding: binary');
- headerData.push('');
- headerData.push(userPropsBatchBody);
- headerData.push('');
- headerData.push('--batch_' + boundryString + '--');
- return headerData.join('\r\n');
- }
-
-
-
-
-
- function parseResponse(batchResponse) {
-
- var results = grep(batchResponse.split("\r\n"), function (responseLine) {
- try {
- return responseLine.indexOf("{") != -1 && typeof JSON.parse(responseLine) == "object";
- }
- catch (ex) { }
- }, null);
-
-
- return results.map(function (result) {
- return JSON.parse(result);
- });
- }
-
-
-
-
-
-
- var grep = function (elems, callback, invert) {
- var callbackInverse,
- matches = [],
- i = 0,
- length = elems.length,
- callbackExpect = !invert;
-
-
-
- for (; i < length; i++) {
- callbackInverse = !callback(elems[i], i);
- if (callbackInverse !== callbackExpect) {
- matches.push(elems[i]);
- }
- }
-
- return matches;
- };
-
-
-
-
- function getBoundryString() {
- return "vrd_" + Math.random().toString(36).substr(2, 9);
- }
-
- var makeBatchRequests = ({ rootUrl, batchUrls, FormDigestValue }) => {
- if (FormDigestValue) {
- return internalBatch({ FormDigestValue: FormDigestValue, rootUrl, batchUrls })
- } else {
-
- return fetch(`${rootUrl}/_api/contextinfo`, {
- method: "POST",
- "headers": { "Accept": "application/json;odata=verbose", credentials: "include", }
- }).then(r => r.json()).then(r => {
- return internalBatch({ FormDigestValue: r.d.GetContextWebInformation.FormDigestValue, rootUrl, batchUrls })
-
- });
- }
- }
- var makePostBatchRequests = ({ rootUrl, batchUrls, FormDigestValue }) => {
- if (FormDigestValue) {
- return internalPostBatch({ FormDigestValue: FormDigestValue, rootUrl, batchUrls })
- } else {
-
- return fetch(`${rootUrl}/_api/contextinfo`, {
- method: "POST",
- "headers": { "Accept": "application/json;odata=verbose", credentials: "include", }
- }).then(r => r.json()).then(r => {
- return internalPostBatch({ FormDigestValue: r.d.GetContextWebInformation.FormDigestValue, rootUrl, batchUrls })
-
- });
- }
- }
- var internalPostBatch = ({ FormDigestValue, rootUrl, batchUrls }) => {
-
-
-
-
-
-
- var endpointsArray = batchUrls;
-
-
- var boundryString = getBoundryString();
-
- var changeSetIdString = getBoundryString();
-
-
- var userPropertiesBatchBody = BuildChangeSetRequestBody(changeSetIdString, "ADD", "", endpointsArray);
-
-
- var batchRequestBody = BuildChangeSetRequestHeader(boundryString, changeSetIdString, userPropertiesBatchBody);
-
-
- console.log("==========================================================")
- console.log(userPropertiesBatchBody)
- console.log("==========================================================")
- console.log(batchRequestBody)
- console.log("==========================================================")
- var requestHeaders = {
- credentials: "include",
- 'X-RequestDigest': FormDigestValue,
- 'Content-Type': `multipart/mixed; boundary="batch_${boundryString}"`
- };
- return fetch(`${rootUrl}/_api/$batch`, {
- method: "POST",
- headers: requestHeaders,
- body: batchRequestBody
- }).then(r => r.text()).then(r => {
-
- var results = parseResponse(r);
-
-
-
-
-
- return results;
- })
- }
- var internalBatch = ({ FormDigestValue, rootUrl, batchUrls }) => {
-
-
-
-
-
-
- var endpointsArray = batchUrls;
-
-
- var boundryString = getBoundryString();
-
-
- var userPropertiesBatchBody = buildBatchRequestBody(endpointsArray, boundryString);
-
-
- var batchRequestBody = buildBatchRequestHeader(userPropertiesBatchBody, boundryString);
-
-
-
- var requestHeaders = {
- credentials: "include",
- 'X-RequestDigest': FormDigestValue,
- 'Content-Type': `multipart/mixed; boundary="batch_${boundryString}"`
- };
- return fetch(`${rootUrl}/_api/$batch`, {
- method: "POST",
- headers: requestHeaders,
- body: batchRequestBody
- }).then(r => r.text()).then(r => {
-
- var results = parseResponse(r);
-
-
-
-
-
- return results;
- })
- }
- return { GetBatchAll: makeBatchRequests, PostBatchAll: makePostBatchRequests };
- })();
You can reference this file in your project. This will also work with Spfx and typescript codebase as well.
How to use this Batch Utility for making Batch API Requests in SharePoint Online
Step 1
Prepare an array of Request URLs.
- var arr=[
- "https://brgrp.sharepoint.com/_api/Lists/Getbytitle('PlaceHolderList')/items(212)" ,
- "https://brgrp.sharepoint.com/_api/Lists/Getbytitle('PlaceHolderList')/items(213)" ,
- "https://brgrp.sharepoint.com/_api/Lists/Getbytitle('PlaceHolderList')/items(214)"]
Step 2
Pass the rootUrl or SiteUrl to generate RequestDigest.
- var rootUrl= "https://brgrp.sharepoint.com"
Step 3
Pass the information as below and that's it. It will return the result or Request.
- BatchUtils.GetBatchAll({rootUrl:rootUrl,batchUrls:arr}) .then(r=>console.log(r))
You can see an example of response as per the below request.
Conclusion
In this article, we have learned about the usage of SharePoint $batch REST API easily using BatchUtils.