CRUD Operations for SharePoint Docs Using REST

In this post, I’m going to show how to do basic CRUD (Create, Read, Update, and Delete) operations with documents and SharePoint RESTful web services. Along the way, I’m going to flesh out possibly the world’s ugliest SPA (single page application). I’m only going to talk about the parts of the code that deal with Ajax and the RESTful web services, but I’ll attach the complete source. It’s a wiki page, so you can just drop it in a document library and open it to see how it works (it does try to work with a picture library with a title of Pictures in the current site, if you don’t have one, you can either create one or change the listTitle variable to be the title of another picture library in your site).


What We’re Going to Build

Below is the basic interface. It consists of an un-ordered list of picture names, each with a radio button next to it. And at the bottom is a div that will act as a drop zone for files dragged onto it. This interface demonstrates create and read. The un-ordered list is populated by reading the list items in our Picture library, and you can create by dragging images to the drop zone.
 
I already have some images in the library. If you don’t, the un-ordered list will be empty and the only visible thing in the form will be the drop zone to add new files. I’m not actually restricting files to be images, you can drop any file on the zone and it will get uploaded. In fact, you can point to any document library instead of a picture list, except that some of the code expects there to be a Description field, which exists on picture lists but not normally on document libraries.


Ugly SPA

If you click on one of the radio buttons to select an image, a form opens up below the list which provides edit and delete functionality.


Ugly SPA Edit Form

As I said, it’s not pretty, but this gives me enough interface to show the complete range of CRUD operations for SharePoint document libraries.

The HTML

There is nothing special about the HTML, but since I’m going to be manipulating it as I demonstrate the CRUD operations, I’ll show it here so you can refer back to it. It consists of an un-ordered list to show pictures currently in the library, a small form for edit/delete, and a div with some structure to implement drag and drop files.

As I process the read operation and shove list items into the un-ordered list, I’ll also add some hidden data- attributes to the item to store things like the title, description, id, and etag, that will be needed for other CRUD operations later on, so a list item will end up looking like this:

Ajax Wrapper

I wanted to do this tutorial without any dependencies, which isn’t that hard but doing Ajax with nothing but XMLHttpRequest is not fun. I don’t know any JavaScript developer who would argue otherwise. So the first thing I’m going to do is wrap XMLHttpRequest in a function called ajax, which takes in an options instance similar to the one used by jQuery’s $.ajax, and converts that instance to what XMLHttpRequest needs to perform an ajax request. The options can specify success and error callbacks, but I do not provide a way to use promises instead of callbacks, since that won’t work in IE without external dependencies. So here is my wrapper:

REST Create

Creating a document is fairly simple, but there are a number of headers that have to be just so. The code below does the following:

  • A lot of the work is just constructing the Url. This REST call is getting the list by title, and then calling files/add on the root folder. It passes in two parameters, the file name and a Boolean indicating whether SharePoint should overwrite or fail if a file with the same name already exists. You also have to specify the request parameters @TargetLibrary and @TargetFileName. This is completely redundant; get used to it, there’s a lot of that in the REST API. Note: if you want to load documents to other folders, you can use ‘/_api/web/GetFolderByServerRelativeUrl’, pass in the server relative path to the folder, and call files/add on that.
  • The HTTP verb for the operation should be POST. If you’re actually familiar with REST from elsewhere, you might expect that PUT would work here (and might even be required for ‘overwrite=true’). But if you try PUT, the SharePoint Lists endpoint will return an error saying that PUT is not allowed and you should use POST. Curiously, despite the error return, the operation does succeed and create or overwrite the file. Don’t fight it, just use POST, as ignoring the returned error can only lead to sorrow down the line.
  • I send the header saying I accept “application/json;odata=verbose”. Ideally, your production code should use JSON light and “application/json;odata=nometadata”, which makes the SharePoint RESTful web services less chatty. The exception to that is if you’re on SharePoint 2013, the server hasn’t been configured to use JSON light, and you are not a farm admin and cannot convince a farm admin to configure it. JSON light came out after 2013, so Service Pack 1 is required, and changes to the web.config are required, before you can use it. I happen to be in exactly that position as I write the code for this post. Not my farm, service pack 1 is installed, but dozens of web.config files need to be updated, and it would take an act of congress to get that done just so my REST traffic is a little less chatty. No biggie, verbose is better for figuring stuff out an tutorials anyway, and perfectly fine for production if that’s all you have available to you.
  • I also set the header “X-RequestDigest” to the request digest from the wiki form. This is required for any write operations. The request digest is only good for 30 minutes, so on a SPA, I should probably refresh it too by calling:
    This will refresh to form digest if and only if it is needed, but I haven’t actually done that in this code.
  • And finally I set the “content-length” header to the number of bytes in the buffer, which is binary (ArrayList) here because I’m uploading images.

REST Read

The read operation is a simple GET and does the following:

  • Build the url using the list title. Also, add the request parameter $select, which is set to a comma separated list of internal field names, specifying which fields I want returned.
  • Again I set the header accept to “application/json;odata=verbose”, for the same reasons I used this in the create operation. I’m going to stop pointing this one out, because it’s going to be the same throughout. If you specify “odata=minimalmetadata” or “odata=nometadata” the structure of the returned data is different, so you’d need to adjust the code slightly.
  • There is a fair amount of code in the success callback, but this is just DOM manipulation code to do things like add a list item for each picture, and preserve the currently selected picture if any.

REST Update

The update operation is probably the most complicated. First, I need to specify the data as a JSON string with the following format:

The metadata is only required because I’m using “odata=verbose”. It contains a single property, which is set to the ListItemEntityTypeFullName of the list, which I can get from the Lists endpoint. I do that with the following code (Note: I specifically set $select to ListItemEntityTypeFullName, otherwise it will be deferred, i.e. not available to me):

The returned entity type from this call is “SP.Data.PicturesItem”. At this point you might be tempted to say “I’m only working with pictures, I can just hard code this.” Not so. The list entity type is unique to the list. So if I had a second picture list in the site, it would be something like “SP.Data.PicturesItem2”. So if you want your code to work with any picture list, you need to retrieve this programmatically.

Also, this code is asynchronous, and so is the update method. So I either need to do nested asynchronous calls or get this on startup. Since I only need to get this once, rather than for each update, I get it on startup by wrapping it in some initialization code like so (the function just populates the global listItemEntityTypeFullName):

Now that I have the list entity type, I’m ready to talk about performing an update. The code is below, and it does the following:

  • Constructs the JSON payload in the format described above.
  • The HTTP verb is a MERGE operation.
  • Set the ‘content-Type’ header to ‘application/json;odata=verbose’. The accept header tells the service what kind of data I want back. The content type header tells the service what kind of data I’m passing in. I didn’t need to set this for the binary picture create operation, but I’m passing a JSON encoded object here and I need to tell the service that.
  • Set the ‘X-RequestDigest’ header as described above (I’m going to stop calling this out too, it’s the same for all write operations).
  • Set the header ‘X-HTTP-Method’ to MERGE; like I said earlier there’s a fair amount of redundancy in the REST API.
  • Set the eTag header. This is basically a 1-up version number. It’s purpose is that if I set this to “1” on a merge, and SharePoint sees the current value is “3”, it’s going to give me one of those “Somebody else seems to have updated the item” errors, and say please refresh the page. If you want it to write regardless of eTag, you can pass “*” as the eTag. Also, it’s a bit quirky, but the etag isn’t a number, it’s a numeric string. By that I mean that the value you get for it is a number in double quotes. When I first shoved that into a data- property I ended up with data-etag=””3″”, which isn’t valid HTML. I could get around that by using single quotes, but that’s pretty ugly. I chose to strip out the quotes and add them back when I set the header, which is why my header value is ‘”‘ + etag + ‘”‘. If you forget these quotes, you will get an error every time. Finally, for the read operation above, the etag was only returned as part of the metadata. So if I use “odata=nometadata” I assume I don’t get this. So if I really want to use etags to avoid overwriting a newer version, that might be an argument that sometimes “odata=verbose” makes sense even for production code in an environment where JSON light is available.

Easy peasy, right?

REST Delete

And last but not least, the delete operation. There isn’t much new here, except:

  • This is the only operation that doesn’t use the Lists endpoint. The path is ‘/_api/web/getfilebyserverrelativeurl’, and it gets passed the server relative path to the file.
  • The HTTP verb and the ‘X-HTTP-Method’ header are both set to DELETE.

That’s it for basic CRUD operations on SharePoint documents. In my next post, I’ll redo the ugly SPA using fetch, which will get me back to using promises. The complete source code for the ugly SPA is attached. Just make sure you have a picture library with Pictures as the title, or change the listTitle variable defined in the code. Then drop it in a document library and click on it to play around with it.

UglySPAXMLHttpRequest.zip

Reference

Leave a Comment