Question

I'm hunting around on the web right now for some basic (preferably working) examples of making a batch of get requests using the new $batch endpoint in O365 with the REST API.

I've located some good information from Andrew Connell and Steve Curran but I'm hoping someone could offer some working JavaScript to demonstrate the concepts.

How do I make a batched REST request using JavaScript to read information from two seperate lists?

Should I immediately look at hooking into a helper library like datajs or Steve Curran's RestBatchExecutor.js or is this basic enough to do using SP.RequestExecutor or jQuery.ajax?

Was it helpful?

Solution

I've been playing around with REST Batching feature recently and found datajs library supports it in a relatively convenient way.

The following example demonstrates how make a batched REST request using JavaScript to read information from two separate lists:

var jsonSPHeaders = {  
    "Accept": "application/json;odata=verbose", 
    "Content-Type": "application/json;odata=verbose",
    "DataServiceVersion": "3.0" 
};

OData.request( {
    requestUri: _spPageContextInfo.webAbsoluteUrl + "/_api/$batch",
    method: "POST",
    headers: { "X-RequestDigest": $("#__REQUESTDIGEST").val(),
               "DataServiceVersion": "3.0" },
    data: { __batchRequests: [
       { requestUri: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Pages')/items", method: "GET" , headers: jsonSPHeaders },
       { requestUri: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Tasks')/items", method: "GET", headers: jsonSPHeaders }
    ]}
},
function (data, response) {
    console.log('Pages list:');
    printListItems(data.__batchResponses[0].data);
    console.log('Tasks list:');
    printListItems(data.__batchResponses[1].data);
}, 
null, 
OData.batchHandler);



function printListItems(data){
   data.results.forEach(function(item){
       console.log(item.Title); 
   });
}

Gist

OTHER TIPS

I've managed to work out how to build the request manually like Andrew Connell does in his SpRestBatchSample on Github (which happens to be how the MSDN example on the topic does as well), but the experience certainly leaves a lot to be desired... I think I will try doing this with breeze next...

The below uses SP.RequestExecutor but assumes that you're requesting data within the same site as where the code is run -- as posted it just logs the response results objects to the console:

SP.SOD.executeFunc("SP.RequestExecutor.js", "SP.RequestExecutor", function () {
    // create a new executor for the current web
    var executor = new SP.RequestExecutor(_spPageContextInfo.webServerRelativeUrl);
    // get a new Guid for the batch using the scripthelper utility
    var batchGuid = SP.ScriptHelpers.newGuid();
    // setup an array to hold strings that will be sent as the request body
    var batchContents = new Array();

    // build first request section against List1
    var endpoint = _spPageContextInfo.webAbsoluteUrl
        + "/_api/web/lists/getbytitle('List1')"
        + "/items";

    batchContents.push("--batch_" + batchGuid);
    batchContents.push("Content-Type: application/http");
    batchContents.push("Content-Transfer-Encoding: binary");
    batchContents.push("");
    batchContents.push("GET " + endpoint + " HTTP/1.1");
    batchContents.push("Accept: application/json;odata=verbose");
    batchContents.push("");

    // build second request section against List2
    var endpoint = _spPageContextInfo.webAbsoluteUrl
        + "/_api/web/lists/getbytitle('List2')"
        + "/items";

    batchContents.push("--batch_" + batchGuid);
    batchContents.push("Content-Type: application/http");
    batchContents.push("Content-Transfer-Encoding: binary");
    batchContents.push("");
    batchContents.push("GET " + endpoint + " HTTP/1.1");
    batchContents.push("Accept: application/json;odata=verbose");
    batchContents.push("");

    // end the request with `--batch_batchid--`
    batchContents.push("--batch_" + batchGuid + "--");

    // join the array contents to build a single batch body string
    var batchBody = batchContents.join("\r\n");

    executor.executeAsync({
        url: _spPageContextInfo.webAbsoluteUrl + "/_api/$batch",
        method: "POST",
        body: batchBody,
        headers: {
            "X-RequestDigest": document.getElementById("__REQUESTDIGEST").value,
            "Content-Type": "multipart/mixed; boundary=batch_" + batchGuid
        },
        success: function (response) {
            console.log(response);
            var responseInLines = response.body.toString().split('\n');
            // read routine below courtesy of Andrew Connel: https://github.com/andrewconnell/sp-o365-rest/tree/master/SpRestBatchSample
            // read each line until you find JSON...
            for (var currentLine = 0; currentLine < responseInLines.length; currentLine++) {
                try {
                    // parse the JSON response...
                    var tryParseJson = JSON.parse(responseInLines[currentLine]);
                    console.log(tryParseJson.d.results);
                } catch (e) {
                    // don't do anything... just keep moving

                    // execution in this block just means we hit a 
                    // line in the response that was not valid JSON -JM
                }
            }
        },
        error: function (response) { console.log(response) }
    });
});

The main reason I think I will try breeze js next is because the responses are given in a mixed text/json format that is fairly ugly to parse through (hence the try...catch I stole and used in my success function...)

One thing that really tripped me up -- in Andrew Connell's example he uses the following syntax on line 197:

batchContents.push(
    'Content-Type: multipart/mixed; boundary="changeset_' + 
    changeSetId + '"');

I had to remove the quotes enclosing the boundary section of the Content-Type value, otherwise I would only receive an empty response from the server to my REST request.

My request content type string wound up looking like

...; boundary=changeset_fcb3e12a-124f-4548-9464-b20902edab92

instead of

...; boundary="changeset_fcb3e12a..."

This may just have something to do with his example using jQuery.ajax and mine using SP.RequestExecutor though

I'll definitely leave the answer open for a while even though this is doing basically what I was looking for -- I think there is some room for improvement.

For SharePoint Online and SharePoint 2016/2019 $batch Rest API is supported. However, $batch REST API supported but implementation is very tricky, and there is very limited resource available online.

Recently I have found an easy way to implement $batch API using a few lines of code using BatchUtils.ts. See below code snippet for CRUD Operation using $batch API.

GET Operation code snippet

//Prepare array of all 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)"];

//Pass rootUrl and array of request as below
BatchUtils.GetBatchAll({rootUrl:"https://brgrp.sharepoint.com",
batchUrls:arr}).then(r=>console.log(r))

CREATE, UPDATE and DELETE Operation Code snippet

// Prepare collection of request with requestUrl and payload data.
var arr=[{
reqUrl:"https://brgrp.sharepoint.com/_api/Lists/Getbytitle('PlaceHolderList')/items(212)"
,action:"UPDATE",
data:{__metadata:{type:"SP.Data.PlaceHolderListListItem"},Title:"Update Article_1"}},
{
reqUrl:"https://brgrp.sharepoint.com/_api/Lists/Getbytitle('PlaceHolderList')/items(213)"
,action:"UPDATE",
data:{__metadata:{type:"SP.Data.PlaceHolderListListItem"},Title:"Update Article_2"}},
{
reqUrl:"https://brgrp.sharepoint.com/_api/Lists/Getbytitle('PlaceHolderList')/items(214)"
,action:"UPDATE",
data:{__metadata:{type:"SP.Data.PlaceHolderListListItem"},Title:"Update Article_3"}},
{
reqUrl:"https://brgrp.sharepoint.com/_api/Lists/Getbytitle('PlaceHolderList')/items"
,action:"ADD",
data:{__metadata:{type:"SP.Data.PlaceHolderListListItem"},Title:"Add Article_1"}}
,{
reqUrl:"https://brgrp.sharepoint.com/_api/Lists/Getbytitle('PlaceHolderList')/items(215)"
,action:"DELETE"}];

BatchUtils.PostBatchAll({rootUrl:"https://brgrp.sharepoint.com",
batchUrls:arr}).then(r=>console.log(r));

Reference links:

  1. SharePoint Batch API Examples
  2. https://www.c-sharpcorner.com/article/sharepoint-batch-request-for-add-list-item/
Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top