Using Fetch with SharePoint Online

Update
If you’re developing on SharePoint Online in the Modern Experience using SharePoint Framework then I would recommend you use a great project like PnPjs or the built in SpHttpClient rather than crafting the requests yourself.


jQuery’s ajax method. If you aren’t a fan of using SP.RequestExecutor, chances are good that $.ajax is used heavily to query and post data to SharePoint’s REST API in your SharePoint customisations.

There’s nothing wrong with jQuery’s $.ajax method. In fact, it’s been incredibly reliable over the past five years for my work. But we must put new technology to the test to keep our skills relevant and understand the current array of options available.

So, Fetch. Fetch? What is that? Why do I care?

Fetch is the modernised and upgraded version of XMLHttpRequest. It’s like $.ajax but native to the browser, meaning that’s one less dependency on jQuery.

Quoting directly from MDN:

The Fetch API provides a JavaScript interface for accessing and manipulating parts of the HTTP pipeline, such as requests and responses. It also provides a global fetch() method that provides an easy, logical way to fetch resources asynchronously across the network.

Please, start by reading the following articles that fully explain what Fetch is, and how it utilises promises.
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
https://developers.google.com/web/fundamentals/getting-started/primers/promises

Looks great, right? So let’s use it.
Oh, wait. We need to support our IE10 users if we’re on the SharePoint 2013 platform. Polyfills to the rescue:

Right, you’ve got those included in your project? Great, we’re ready.
Here’s some sample code to get you started:


var myHeaders = new Headers({
'X-RequestDigest': document.getElementById('__REQUESTDIGEST').value,
'Accept': 'application/json; odata=verbose'
})
var myInit = {
method: 'GET',
headers: myHeaders,
credentials: 'include'
}
fetch(_spPageContextInfo.webAbsoluteUrl + "/_api/web", myInit).then(function(response) {
if (response.ok) {
return response.json();
}
else {
throw new Error('Network response was not ok.');
}
}).then(function(data) {
if (window.console) {
console.log(data);
}
}).catch(function(error) {
// Error handling code goes here
})

This is just a simple GET request. Paste it into your DevTools of choice and inspect the response and you’ll be back in familiar territory.

That’s all for now. A future post will detail these common GET and POST requests using Fetch:

  • Query list items
  • Create new item
  • Update an existing item
  • Delete an existing item

UPDATE 2017-05

Create a new list item using Fetch

  1. GET the ListItemEntityTypeFullName using the REST API endpoint “/_api/lists/getbytitle(‘Sample List’)”
  2. POST our JSON payload to the REST API endpoint “/_api/lists/getbytitle(‘Sample List’)/items”

Remember to use “application/json; odata=verbose” as your Content-Type and Accept headers.
If you forget, SharePoint will very unhelpfully let you know that __metadata doesn’t exist on the type SP.Data.Sample_x0020_ListListItem.

If you have any trouble please refer to this post on StackOverflow: https://stackoverflow.com/questions/29775797/fetch-post-json-data


var getHeaders = new Headers({
'X-RequestDigest': document.getElementById('__REQUESTDIGEST').value,
'Accept': 'application/json; odata=verbose'
})
var getOptions = {
method: 'GET',
headers: getHeaders,
credentials: 'include'
}
fetch(_spPageContextInfo.webAbsoluteUrl + "/_api/lists/getbytitle('Sample List')?$select=ListItemEntityTypeFullName", getOptions).then(function(response) {
if (response.ok) {
return response.json();
}
else {
throw new Error('Network response was not ok.');
}
}).then(function(data) {
// Our new list item metadata
var metadata = {
Title: 'New List Item',
__metadata: {
type: data.d.ListItemEntityTypeFullName
}
}
// POST request headers
var postHeaders = new Headers({
'X-RequestDigest': document.getElementById('__REQUESTDIGEST').value,
'Accept': 'application/json; odata=verbose',
'Content-Type': 'application/json; odata=verbose'
});
// POST request options
var postOptions = {
method: 'POST',
headers: postHeaders,
credentials: 'include',
body: JSON.stringify(metadata)
}
// Return the fetch result so we can continue chaining function calls
return fetch(_spPageContextInfo.webAbsoluteUrl + "/_api/lists/getbytitle('Sample List')/items", postOptions);
}).then(function(response) {
if (response.ok) {
// we all good
console.log('Response was ok!')
}
else {
throw new Error('Network response was not ok.');
}
}).catch(function(error) {
// Error handling code goes here
console.error(error);
alert('Error has been logged to the console');
})

Update 2017-06

Update an existing list item using Fetch

The important parts are lines 33 and 34 where we specify that we are performing a MERGE, i.e. a list item update, and that we don’t care what the current list item eTag number is.

'X-HTTP-Method': 'MERGE', // important - ensure MERGE is in all caps
'If-Match': '*' // important - * signifies we don't care what the current eTag number is

In the event that you need to ensure that your update doesn’t conflict with any other updates that may occur at the same time, read on.

You will need to GET the list item, with the header ‘Accept’ set to ‘application/json; odata=verbose’, and use the eTag value retrieved in the GET request in your POST request at a later time.

If there was a change to that list item between the retrieval of the eTag value, e.g. when a user opens a custom form, and the eventual POST request to update the list item, e.g. user saves the custom form, then the POST request will fail with error code 412 (Precondition failed).

The full code can be found below.


var getHeaders = new Headers({
'X-RequestDigest': document.getElementById('__REQUESTDIGEST').value,
'Accept': 'application/json; odata=verbose'
})
var getOptions = {
method: 'GET',
headers: getHeaders,
credentials: 'include'
}
fetch(_spPageContextInfo.webAbsoluteUrl + "/_api/lists/getbytitle('Sample List')/Items(1)", getOptions).then(function(response) {
if (response.ok) {
return response.json();
}
else {
throw new Error('Network response was not ok.');
}
}).then(function(data) {
// Our updated list item metadata
var metadata = {
Title: 'Updated List Item',
__metadata: {
type: data.d.__metadata.type
}
}
// POST request headers
var postHeaders = new Headers({
'X-RequestDigest': document.getElementById('__REQUESTDIGEST').value,
'Accept': 'application/json; odata=verbose',
'Content-Type': 'application/json; odata=verbose',
'X-HTTP-Method': 'MERGE', // important – ensure MERGE is in all caps
'If-Match': data.d.__metadata.etag
});
// POST request options
var postOptions = {
method: 'POST',
headers: postHeaders,
credentials: 'include',
body: JSON.stringify(metadata)
}
// Return the fetch result so we can continue chaining function calls
return fetch(_spPageContextInfo.webAbsoluteUrl + "/_api/lists/getbytitle('Sample List')/Items(1)", postOptions);
}).then(function(response) {
if (response.ok) {
// we all good
console.log('Response was ok!')
}
else {
throw new Error('Network response was not ok.');
}
}).catch(function(error) {
// Error handling code goes here
console.error(error);
alert('Error has been logged to the console');
})

Delete an existing item using Fetch


// POST request headers
var postHeaders = new Headers({
'X-RequestDigest': document.getElementById('__REQUESTDIGEST').value,
'Accept': 'application/json; odata=verbose',
'X-HTTP-Method': 'DELETE', // important – ensure DELETE is in all caps
'If-Match': '*'
});
// POST request options
var postOptions = {
method: 'POST',
headers: postHeaders,
credentials: 'include'
}
fetch(_spPageContextInfo.webAbsoluteUrl + "/_api/lists/getbytitle('Sample List')/Items(1)", postOptions).then(function(response) {
if (response.ok) {
// we all good
console.log('Response was ok!')
}
else {
throw new Error('Network response was not ok.');
}
}).catch(function(error) {
// Error handling code goes here
console.error(error);
alert('Error has been logged to the console');
})

One thought on “Using Fetch with SharePoint Online

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.