Batch Query Using sp-pnp-js

In today's coding world, most of the developers think that everything should be reusable. Based on this point, I am writing this article for reusable batch query in SPFx using SP-Pnp-Js.

Problem Statement

 
In general, if we want to send multiple queries to SharePoint, we use a batch statement like below.
 
Example
 
If I want to filter multiple lists with the below queries in page load/any other events.
  1. Loading all lists available in the site.
  2. Check available of list “XYZ”
  3. Employee list whose department is “IT”
  4. Retrieving regional settings information.
Generally, we will follow the following way.
  1. let batch = pnp.sp.createBatch();  
  2.    // BaseTemplate=100 is for custom lists  
  3.    pnp.sp.web.lists.filter(“Hidden eq false and BaseTemplate eq 100”).get().inBatch(batch).then((listsResult)=>{  
  4.    });  
  5.    pnp.sp.web.lists.getByTitle(“XYZ”).inBatch(batch).then((xyzResult)=>{  
  6.    });  
  7.    pnp.sp.web.lists.getByTitle(“Employee”).filter(“dept eq ‘IT’”).get().inBatch(batch).then((employeeResult)=>{  
  8.    });  
  9.    pnp.sp.web.regionalSettings.timeZone.get().inBatch(batch).then((timeZoneResult)=>{  
  10.    });  
  11. batch.execute().then((data) => {  
  12. });  
The above code will work and give the expected result, but here, if the number of batch operations increases, the code itself will be odd to read (if we consider reusability of the code as developer’s main goal).
 

Resolution

 
Here, we need to think about the usage of For loops in this scenario. So, we require an array/object for looping all the queries, if we consider the below portion of code as an array object.
 

Code Implementation Process

 
If I want to retrieve data from a SharePoint list (single query) based on the implemented code, I will follow the below process.
 
In SP-PnP-JS, web is an object of O365 Web(pnp.sp.web). This object has lists property and it is an object of Lists class(pnp.sp.web.lists). This lists property has a method named getByTitle(pnp.sp.web.lists.getByTitle("XYZ")) which accepts list name as a parameter and which will return the slected list as List class Object. This List Class has property named items which will return Items class object(pnp.sp.web.lists.getByTitle("XYZ").items) . This Items class is extending SharePointQuerable class (so all methods in this are internally used for our original items class) which has get method which will return our data.
 
Based on the above process of execution and based on JavaScript object feasibility, I thought if we form these all into one array object, which will have information about our query chaining. So, I have just followed the below way of approach to achieve our requirement.
 
For getting data in a SharePoint List (single query), we need to follow the below steps for forming input array object for reusable method.
  1. By default, we need to create SharePoint Web object (for any kind of GET operations we need to have this is a basic object)
    1. let webObject = pnp.sp.web; 
  2. With the help above web object, our next goal to access lists property which will return an object Lists class, so I have used this is my first string in the array.
    1. let batchLoadQueries = [“lists"];  
  3. Based on above Lists class, it has method getByTitle() which accepts list name as a parameter and returns selected list, so it needs two parameters under one array index so we can use this as key-value pair and push it to our above batchLoadQueries array and our array will be as below.
    1. ["lists", { "getByTitle""Employee" }]  
  4. Based on the above query, we are navigated up to lists, so we need to retrieve the items of the list. It is the single property that doesn’t need any kind of key-value pair. So array will be like below
    1. ["lists", { "getByTitle""Employee" },"items"]  
  5. Up to the above object, it will return items, If it has any kind query/select and these two are key-value pairs so we need to form array as below
    1. ["lists", { "getByTitle""Employee" }, "items", { "filter""Dept eq IT" }]  
If we want to send multiple batch statements, we need to form array of array objects.
 
Eg
  1. let batchLoadQueries = [[“lists", { "filter": "Hidden eq false and BaseTemplate eq 100" }],["lists", { "getByTitle": "XYZ" }], ["lists", { "getByTitle": "Employee" }, "items", { "filter": "Dept eq IT" }], ["regionalSettings", "timeZone"]];  
Reusable Method(Complete Code please download from attachment)
  1. public FormQueryObject(currentBatch) {  
  2.         let webObject = pnp.sp.web;  
  3.         let currentQueryObject = webObject;  
  4.         let batchObject = {};  
  5.         for (var subIndex = 0; subIndex < currentBatch.length; subIndex++) {  
  6.             let queryObject = (subIndex == 0) ? currentQueryObject[currentBatch[subIndex]] : currentQueryObject;  
  7.             if (currentBatch[subIndex] != null && typeof currentBatch[subIndex] == "object") {  
  8.                 for (var key in currentBatch[subIndex]) {  
  9.                     if (currentBatch[subIndex].hasOwnProperty(key)) {  
  10.                         if (key == "orderBy")  
  11.                             currentQueryObject = queryObject[key](currentBatch[subIndex][key].Name, currentBatch[subIndex][key].Value);                          
  12.                         else {  
  13.                             currentQueryObject = queryObject[key](currentBatch[subIndex][key]);  
  14.                         }  
  15.                     }  
  16.                 }  
  17.             }  
  18.             else  
  19.                 currentQueryObject = (subIndex == 0) ? queryObject : queryObject[currentBatch[subIndex]];  
  20.         }  
  21.         batchObject["currentQueryObject"] = currentQueryObject;  
  22.         return batchObject;  
  23.     }  
  24. public executeBatch(batchPromises: any[]): Promise<any> {  
  25. let batch = pnp.sp.createBatch();  
  26. let sendBatchRequest = new Array<any>();  
  27.         let batchResult = new Array<any>();  
  28.   
  29. for (var batchIndex = 0; batchIndex < batchPromises.length; batchIndex++) {  
  30.       let currentBatch = batchPromises[batchIndex];  
  31.       let batchObject = this.FormQueryObject(currentBatch);  
  32.       let currentQueryObject = batchObject["currentQueryObject"];  
  33.      sendBatchRequest.push(currentQueryObject.inBatch(batch).get().then((resultSet) =>               {  
  34.                     batchResult.push(resultSet);  
  35.                 }).catch((error: any) => {  
  36.                     batchResult.push(false);  
  37.                 }));  
  38.   
  39. }  
  40. return new Promise<any>((resolve: (batchResult: any) => void, reject: () => void) => {  
  41.             batch.execute().then((data) => {  
  42.          
  43.              resolve(batchResult);  
  44.          
  45.      }).catch((error: any) => {  
  46.                 resolve(false);  
  47.             });  
  48.         });  
  49.   
  50. }  
  51. this. executeBatch(batchLoadQueries).then((result)=>{  
  52.   
  53. });