HomeAbout

Vanilla JS and SharePoint REST API

By Denis Molodtsov
Published in SharePoint
January 30, 2025
1 min read
Vanilla JS and SharePoint REST API

Table Of Contents

01
The Problem
02
Solution: JavaScript in the Browser Console for a Quick Report
03
Conclusion

The Problem

Sometimes, using PnP PowerShell just isn’t an option. Whether it’s because of restrictions in your environment or you simply need a quick peek at your SharePoint data, you need another way. I often find myself needing a fast report on lists, fields, subsites, etc.

Solution: JavaScript in the Browser Console for a Quick Report

Enter JavaScript! I know, it might sound a bit unconventional, but running JavaScript right in the browser console has come to the rescue more than once. This method gives me a quick and dirty report on the fly—no extra tools required. It’s not the most polished approach out there, but when you need to discover your SharePoint data fast, it really gets the job done.

Get List of Lists and Content Types

Below is a fun little script that does the heavy lifting for you. It starts at your current SharePoint site, grabs all the subsites (webs) recursively, and then pulls in every list along with its content types. Once it’s done, it shows you a neat table right in your browser’s console and even downloads a CSV file for you. Super handy when you need a quick report!

/*************************************************************
* 1) MAIN ENTRY POINT:
* Start from the current site, get all webs (subsites),
* then get lists from each web, build final data, and export.
*************************************************************/
fetchWebAndSubsites(_spPageContextInfo.webAbsoluteUrl)
.then(function(allWebs) {
// For each web, retrieve its lists
var allListPromises = allWebs.map(function(web) {
return fetchWebLists(web.Url).then(function(lists) {
return lists.map(function(list) {
var cts = list.ContentTypes && list.ContentTypes.results
? list.ContentTypes.results.map(function(ct) { return ct.Name; }).join(", ")
: "";
return {
"Site Title": web.Title,
"Site URL": web.Url,
"List Title": list.Title,
"List URL": list.DefaultViewUrl,
"Content Types": cts
};
});
});
});
// Wait until all list data is fetched
return Promise.all(allListPromises).then(function(websListData) {
// Flatten the array of arrays
var finalData = [].concat.apply([], websListData);
// Show table in console
console.table(finalData);
// Convert to CSV
var csv = convertToCSV(finalData);
// Automatically download CSV
downloadCSV(csv, "AllSubsitesLists.csv");
});
})
.catch(function(error) {
console.error("Error retrieving webs/lists:", error);
});
/*************************************************************
* 2) RECURSIVELY FETCH ALL SUBSITES (webs)
* Starting from a given web URL, gather that web’s info,
* then recursively iterate its child webs.
*************************************************************/
function fetchWebAndSubsites(webUrl) {
// We'll collect all webs in an array, including the current one
var allWebs = [];
return fetchWebInfo(webUrl)
.then(function(currentWeb) {
// Add the current web to the array
allWebs.push({ Title: currentWeb.Title, Url: currentWeb.Url });
// Now get its direct child webs
return fetchSubWebs(webUrl);
})
.then(function(subwebs) {
// For each child web, recursively call fetchWebAndSubsites
var childWebPromises = subwebs.map(function(sw) {
return fetchWebAndSubsites(sw.Url);
});
// Wait for all children to return their webs
return Promise.all(childWebPromises);
})
.then(function(childWebArrays) {
// childWebArrays is an array of arrays, flatten them
childWebArrays.forEach(function(arrayOfWebs) {
allWebs = allWebs.concat(arrayOfWebs);
});
return allWebs;
});
}
/**
* Fetch the current web’s Title and Url
*/
function fetchWebInfo(webUrl) {
var url = webUrl + "/_api/web?$select=Title,Url";
return fetch(url, {
method: "GET",
headers: { "Accept": "application/json;odata=verbose" }
})
.then(function(response) {
if (!response.ok) {
throw new Error("Failed to fetch web info for: " + webUrl);
}
return response.json();
})
.then(function(data) {
return data.d; // { Title, Url }
});
}
/**
* Fetch direct child webs (subsites) of a given web
*/
function fetchSubWebs(webUrl) {
var url = webUrl + "/_api/web/webs?$select=Title,Url";
return fetch(url, {
method: "GET",
headers: { "Accept": "application/json;odata=verbose" }
})
.then(function(response) {
if (!response.ok) {
throw new Error("Failed to fetch sub webs for: " + webUrl);
}
return response.json();
})
.then(function(data) {
return data.d.results; // an array of { Title, Url }
});
}
/*************************************************************
* 3) FETCH LISTS FOR A GIVEN WEB
* We return each list’s Title, DefaultViewUrl, and ContentTypes.
*************************************************************/
function fetchWebLists(webUrl) {
var url = webUrl
+ "/_api/web/lists?$select=Title,DefaultViewUrl,ContentTypes/Name"
+ "&$expand=ContentTypes";
return fetch(url, {
method: "GET",
headers: { "Accept": "application/json;odata=verbose" }
})
.then(function(response) {
if (!response.ok) {
throw new Error("Failed to fetch lists for: " + webUrl);
}
return response.json();
})
.then(function(data) {
return data.d.results; // array of list objects
});
}
/*************************************************************
* 4) CSV CONVERSION / DOWNLOAD
*************************************************************/
/**
* Converts an array of objects into CSV format.
* Automatically wraps fields containing commas or quotes
* in quotes and escapes internal quotes.
*/
function convertToCSV(rows) {
if (!rows || !rows.length) {
return "";
}
// Extract headers (keys) from the first item
var headers = Object.keys(rows[0]);
var csvLines = [];
// Add header line
csvLines.push(headers.join(","));
// Add each row
rows.forEach(function(row) {
var values = headers.map(function(header) {
var val = row[header] ? row[header].toString() : "";
// If the value contains a comma or quote, wrap in quotes & escape quotes
if (/[",]/.test(val)) {
val = '"' + val.replace(/"/g, '""') + '"';
}
return val;
});
csvLines.push(values.join(","));
});
return csvLines.join("\n");
}
/**
* Initiates a download of the CSV file in the browser.
*/
function downloadCSV(csvContent, filename) {
var blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
var link = document.createElement("a");
var url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", filename);
link.style.visibility = "hidden";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}

Results:

Get list of lists and content types
Get list of lists and content types

Conclusion

And that’s it! This approach is a quick and dirty solution that just works. It might not be the most elegant or polished approach out there, but sometimes you need something fast that gets the job done. With just a bit of vanilla JavaScript and the SharePoint REST API, you can pull all your subsites, lists, and content types, display them in a neat console table, and even download a CSV report—all right from your browser.


Tags

SharePointJavaScriptRest API

Share

Previous Article
Retain SharePoint Online Logs
Denis Molodtsov

Denis Molodtsov

Microsoft 365 Architect

Related Posts

Read Retained SharePoint Online Logs from Blob Storage
Read Retained SharePoint Online Logs from Blob Storage
February 10, 2025
2 min

Quick Links

About

Social Media