Manipulating list items in SharePoint Hosted Apps using the REST API
In this post I will describe how to use the REST API to manipulate list items from within a SharePoint hosted App. For information on the different hosting options available (Azure Auto hosted and Provider Hosted Apps) there is a good MSDN article here.
Introduction
The REST API in SharePoint 2013 provides developers with a simple standardised method of accessing information contained within SharePoint. It can be used from any technology that is capable of sending standard http requests and is particularly useful for developers who are not familiar with the Client Side Object Model. For a full list of advantages and disadvantages of the REST API, and for a comparison with other API’s click here.
This article does not cover App Permissions. For information about permissions there is a good MSDN article here. Use of the REST API in other hosting options, whilst similar to SharePoint Hosted Apps, is not covered in this article.
Obtaining the site URI
One key prerequisite to interacting with SharePoint lists items is to obtain the correct URI for the SharePoint site the list is hosted in. There are multiple ways to do this:
- On SharePoint pages _spPageContextInfo.siteAbsoluteUrl contains the URI to the site that the app is installed within (the parent site).
- You can include a set of standard tokens in the URL attribute of both pages and web parts. Adding {StandardTokens} will include the following 7 parameters to the url; SPHostUrl, SPHostTitle, SPAppWebUrl, SPLanguage, SPClientTag, SPProductNumber, and SenderId. These will need to be split out of the URL to be used. There are multiple ways to do this. The simplest is to loop through all the parameters until you find the parameter you are looking for. The following snippet of code searches for the SPAppWebUrl.
- var params = document.URL.split("?")[1].split("&");
- for (var i = 0; i < params.length; i = i + 1) {
- var param = params[i].split("=");
- switch (param[0]) {
- case"ParameterName":
- Variable = decodeURIComponent(param[1]);
- break;
- // add additional cases for other parameters here, for example:
- // case "OtherParameterName":
- // DifferentVariable = parseInt(decodeURIComponent(param[1]));
- // break;
- }
- }
There are several different tokens that you can include in the url's used to open pages within your App. For more information about the different tokens and some basic information about each of them click here. To add the tokens to your start page you can use the visual designer for the App's manifest file. The setting can be found on the ‘General’. It is worth noting that the manifest editor has changed significantly since the original release of the developer tools. If you are using an old version of the tools then this setting may be in a different place.
You can also add the tokens using the source code editor. The following example shows the start page url where the standard tokens have been added (note that ?{StandardTokens} has been appended to the StartPage url):
- <Properties>
- <Title>Example CRUD Operations</Title>
- <StartPage>~appWebUrl/Pages/Default.aspx?{StandardTokens}</StartPage>
- </Properties>
REST URI Operators
Once you have the correct base URI the next step is to determine the correct URI for the list with which you would like to interact. The REST endpoint exposes the same GetByTitle() method that the Client Side Object Model offers and can be used by appending “/_api/web/lists/getbytitle(title)” to the end of the base URI. More information about the REST endpoint can be found in this MSDN article.
List Item Types
To update or create list items you will need to include the list item type. This is a string automatically created by SharePoint when the list is first created. Two possible ways to obtain this are:
- Perform a read operation on the list and locate the type within the returned set of elements. In JSON format the type can be found within the __metadata attribute of all of the returned list items. In Atom/xml format (the default format returned if you query from a web browser) you can find it in the category scheme attribute (labelled ‘term’) within the item.
- You can attempt to generate the value from the list name. In general list item types follow the convention SP.Data.ListNameListItem (e.g. list name is “Test”, list item type is SP.Data.TestListItem). However this is not always the case. For example SharePoint automatically capitalises the first letter of the list name (e.g. list name “test” list item type is SP.Data.TestListItem). The following code snippet shows how to generate the List Item Type based on the list name.
- function GetItemTypeForListName(name) {
- return"SP.Data." + name.charAt(0).toUpperCase() + name.slice(1) + "ListItem";
- }
Request Digests
A replay attack occurs when a valid request is sent to the server and a copy of that request is stored. The request is then sent multiple times (replayed). This can result in a variety of different issues the most common of which is duplication of data. To prevent this, SharePoint requires the user to include a request digest value with each create, update and delete operation. This value is then used by SharePoint to identify non-genuine requests. Inside SharePoint Hosted apps, the request digest can be found by reading the value of the “__REQUESTDIGEST” object within the html page.
- var digestValue = $("#__REQUESTDIGEST").val()
Note: this will only work on pages that contain the SharePoint Chrome element. The example above requires JQuery to work.
eTags
When updating or deleting items within SharePoint lists via REST you must specify the Entity Tag (eTag) value that was returned with the item during the initial query. This enables SharePoint to determine if the item has changed since it was requested. Alternatively you can tell SharePoint to perform the operation regardless by specifying * as the eTag value. For example:
- “If-Match”: item.__metadata.etag can be used to specify the actual eTag value (‘item’ is the object returned from SharePoint containing the list item in JSON format).
- “If-Match”: “*” can be used to match any eTag value resulting in the operation being performed regardless of the actual value.
The examples above can be found in the code sample at the end of this article. They form part of the Ajax call for updating an item. eTags are part of the HTTP Protocol V1.1, more information on the topic can be found here.
Cross Domain Calls
Cross domain calls are blocked by modern web browsers due to security concerns. This is a common issue in web based development and is particularly relevant due to the nature of SharePoint hosted Apps. For example, when accessing data in the parent web (https://Sharepoint.com) from an App (hosted at https://Apps.SharePoint.com/MyApp) the calls will be blocked. To get round this you can use the SP.RequestExecutor.js script to relay messages to SharePoint from within the same domain.
To do this you will need to load the JavaScript file from the host web and then use it to execute the REST queries across the network. The following example shows how to do this.
- var SPHostUrl;
- var SPAppWebUrl;
- var ready = false;
- // this function is executed when the page has finished loading. It performs two tasks:
- // 1. It extracts the parameters from the url
- // 2. It loads the request executor script from the host web
- $(document).ready(function () {
- var params = document.URL.split("?")[1].split("&");
- for (var i = 0; i < params.length; i = i + 1) {
- var param = params[i].split("=");
- switch (param[0]) {
- case"SPAppWebUrl":
- SPAppWebUrl = decodeURIComponent(param[1]);
- break;
- case"SPHostUrl":
- SPHostUrl = decodeURIComponent(param[1]);
- break;
- }
- }
- // load the executor script, once completed set the ready variable to true so that
- // we can easily identify if the script has been loaded
- $.getScript(SPHostUrl + "/_Layouts/15/SP.RequestExecutor.js", function (data) {
- ready = true;
- });
- });
- // this function retrieves the items within a list which is contained within the parent web
- function GetItems() {
- // only execute this function if the script has been loaded
- if (ready) {
- // the name of the list to interact with
- var listName = "MyList";
- // the url to use for the REST call.
- var url = SPAppWebUrl + "/_api/SP.AppContextSite(@target)" +
- // this is the location of the item in the parent web. This is the line
- // you would need to change to add filters, query the site etc
- "/web/lists/getbytitle('" + listName + "')/items?" +
- "@target='" + SPHostUrl + "'";
- // create new executor passing it the url created previously
- var executor = new SP.RequestExecutor(SPAppWebUrl);
- // execute the request, this is similar although not the same as a standard AJAX request
- executor.executeAsync(
- {
- url: url,
- method: "GET",
- headers: { "Accept": "application/json; odata=verbose" },
- success: function (data) {
- // parse the results into an object that you can use within javascript
- var results = eval(JSON.parse(data.body));
- },
- error: function (data) {
- // an error occured, the details can be found in the data object.
- alert("Ooops an error occured");
- }
- });
- }
- }
Note: the middle line of the URL variable is where you would change the address that the REST call is sent to. For example to get information about a list rather than its items you can remove “/items” leaving "/web/lists/getbytitle('" + listName + "')?"
Example Operations
This section contains sample code for all of the CRUD operations. All of this sample code can be found in the source code which is available for download. The request executor is not needed in this example since the site URL is in the same domain as the app.
Create
The following snippet of code shows how to perform a Create operation against a SharePoint list. The first line of the function is a call to a method which returns a list item type based on the name of the list using the rules mentioned previously. The next 4 lines create a JavaScript object which contains the information about the item that should be created including values for each of the fields and the item type. The reminder of the function executes the ajax call and then calls either the success or failure functions (passed into the function as parameters).
- // CREATE Operation
- // listName: The name of the list you want to get items from
- // siteurl: The url of the site that the list is in. // title: The value of the title field for the new item
- // success: The function to execute if the call is sucesfull
- // failure: The function to execute if the call fails
- function createListItemWithDetails(listName, siteUrl, title, success, failure) {
- var itemType = GetItemTypeForListName(listName);
- var item = {
- "__metadata": { "type": itemType },
- "Title": title
- };
- $.ajax({
- url: siteUrl + "/_api/web/lists/getbytitle('" + listName + "')/items",
- type: "POST",
- contentType: "application/json;odata=verbose",
- data: JSON.stringify(item),
- headers: {
- "Accept": "application/json;odata=verbose",
- "X-RequestDigest": $("#__REQUESTDIGEST").val()
- },
- success: function (data) {
- success(data);
- },
- error: function (data) {
- failure(data);
- }
- });
- }
Read
Reading items is probably the simplest of all of the CRUD operations. The code below shows the simplest form of a read operation. A simple ajax call is executed against the SharePoint server. If the call succeeds then the success function (passed in as a parameter) is called, if it fails then the failure function (also passed in as a parameter) is called. In both cases the data returned from the server is passed through to the function. The code sample also includes an example of how to retrieve an item based on its ID.
- // READ opperation
- // listName: The name of the list you want to get items from
- // siteurl: The url of the site that the list is in.
- // success: The function to execute if the call is sucesfull
- // failure: The function to execute if the call fails
- function getListItems(listName, siteurl, success, failure) {
- $.ajax({
- url: siteurl + "/_api/web/lists/getbytitle('" + listName + "')/items",
- method: "GET",
- headers: { "Accept": "application/json; odata=verbose" },
- success: function (data) {
- success(data);
- },
- error: function (data) {
- failure(data);
- }
- });
- }
Update
In contrast to reading list items updating them is probably the hardest operation. To update an item you will need to obtain the URL of the item and, depending on your handling of eTags, the eTag value. The simplest way to do this is to perform a get operation and then use the properties of the item. The update method uses the same method mentioned for update operations to generate the list item type.
The first line of this function calls a method to get the list item type for the specified list. The next four lines create a JavaScript object containing details of the list item. The next line is a call to a get operation (similar to the one described previously) which returns the list item to be updated. This allows us to extract the eTag and item url and makes it easier to update. If this is successful then the call to update the item is executed and either the success or failure function is executed depending on the result.
- // Update Operation
- // listName: The name of the list you want to get items from
- // siteurl: The url of the site that the list is in. // title: The value of the title field for the new item
- // itemId: the id of the item to update
- // success: The function to execute if the call is sucesfull
- // failure: The function to execute if the call fails
- function updateListItem(itemId, listName, siteUrl, title, success, failure) {
- var itemType = GetItemTypeForListName(listName);
- var item = {
- "__metadata": { "type": itemType },
- "Title": title
- };
- getListItemWithId(itemId, listName, siteUrl, function (data) {
- $.ajax({
- url: data.__metadata.uri,
- type: "POST",
- contentType: "application/json;odata=verbose",
- data: JSON.stringify(item),
- headers: {
- "Accept": "application/json;odata=verbose",
- "X-RequestDigest": $("#__REQUESTDIGEST").val(),
- "X-HTTP-Method": "MERGE",
- "If-Match": data.__metadata.etag
- },
- success: function (data) {
- success(data);
- },
- error: function (data) {
- failure(data);
- }
- });
- }, function(data){
- failure(data);
- });
- }
Delete
The code below performs a delete operation. In a similar way to updating items delete operations are performed using the URI of the item and depending on how eTags are handled the eTag value of the item. In this example the item is first retrieved and then its properties (uri and eTag) are used to execute the delete operation.
- // Delete Operation
- // itemId: the id of the item to delete
- // listName: The name of the list you want to delete the item from
- // siteurl: The url of the site that the list is in.
- // success: The function to execute if the call is sucesfull
- // failure: The function to execute if the call fails
- function deleteListItem(itemId, listName, siteUrl, success, failure) {
- getListItemWithId(itemId, listName, siteUrl,function (data) {
- $.ajax({
- url: data.__metadata.uri,
- type: "POST",
- headers: {
- "Accept": "application/json;odata=verbose",
- "X-Http-Method": "DELETE",
- "X-RequestDigest": $("#__REQUESTDIGEST").val(),
- "If-Match": data.__metadata.etag
- },
- success: function (data) {
- success(data);
- },
- error: function (data) {
- failure(data);
- }
- });
- },
- function (data) {
- failure(data);
- });
- }
Source Code
The following downloadable source code contains a working example of all four operations. To run the example you will need to configure the SiteUrl of the project to point to a SharePoint 2013 developer site. To do this click on the project in solution explorer and open up the properties task pane. Enter the URL of your site into the SiteUrl box and then press F5.
The sample contains a page hosted within the App Web which interacts with a list in the Parent Web. When you install the App you will be asked to select a list and trust it. The list you select must contain a Title column (a new custom list will work just fine). To use the App enter the name of the list you want to access data from (this must match the list you selected when you trusted the App) and then use the controls under each of the four headings. The code is all contained within the App.js file of the project.
Please Note: to run this sample you will also need access to a SharePoint server which has been configured for Apps. The simplest way to do this is to create an Office 365 developer account. Alternatively you can install SharePoint on your own server. There is a guide to configuring SharePoint 2013 servers for Apps here.
Comments
Anonymous
March 28, 2013
Downloaded your sample, very useful. However your method GetItemTypeForListName(name) which is a bit of string manipulation, doesn't work if the internal name and display name are different. I found I had to do an ajax call to the following rest URL var url = siteUrl + "/_api/web/lists/getbytitle('" + listName + "')?$select=ListItemEntityTypeFullName"; To ensure I always had the correct ItemType Name.Anonymous
March 28, 2013
any idea how to update lookup columns using the 2013 rest apiAnonymous
March 28, 2013
Hi Russell lookup columns are interesting. If you perform a read operation on your list items and then look at the columns that are returned then you will notice that you get an ID for your lookup (i.e. if your lookup column is call MyLookup then you would see a column called MyLookupId). Assuming you know the Id of the item you want as your lookup value you can then set this field in the request equal to the Id and perform the operation. I hope this helps !Anonymous
April 23, 2013
The comment has been removedAnonymous
June 13, 2013
Hi James, Is possible to delete multiple list Items in single REST requestAnonymous
August 28, 2013
I am using update operation on list... ajax call returning success but item is not updated.. please help me with thisAnonymous
September 20, 2013
Is it possible to create items of a specific content type? I can successfully create an item but it's of type item. I have a custom CT enabled on my list that I would like to create the item as. Great post - helped me tremendously for sure!Anonymous
October 03, 2013
nice article. is it possible to query list items other than HostWeb and AppWeb using REST?Anonymous
March 24, 2014
Can u provide the above code for C#Anonymous
May 28, 2014
can you please provide the above code in c# for delete operationAnonymous
June 02, 2014
How can I add list item to List from the user who is having Read Pemission on the List.Anonymous
June 11, 2014
Thank you for the excellent post. I was wondering if you could help as I am having trouble in constructing the URL to filter SharePoint 2010 Announcement list items that have not expired. Here is what I have but I couldn't find a way to solve it as there seem no way of handling Today's date: http://[My SP 2010 site]/_vti_bin/listdata.svc/Announcements?$select=Title,Created,Expires&$filter=Expires ge (Today) ThanksAnonymous
July 24, 2014
Awesome bro.. too good explanation :)Anonymous
September 10, 2014
I need to copy certain specified columns of list items from one list to another on SharePoint. Does anyone know how i can do this? The names of the columns on the source list and destination list are different. I also need to check that the list item being moved does not already exist in the destination list. Any help is highly appreciated.Anonymous
November 12, 2014
WOW...that was detail note...great articleAnonymous
July 14, 2015
Is it possible to create an item inside folder in SharePoint list? I have enabled folder creation on sharepoint list but not able to create list item inside folder using rest api. Appreciate any help.