Sharepoint - Basic batched get requests using REST API

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


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.