Moving from SPServices to REST, Part 2: New Patterns for SPServices Development

This entry is part 2 of 6 in the series Moving from SPServices to REST

Summary: You can improve your SPServices-based code today to prepare for converting to REST calls later. In this article, Marc introduces some new patterns for your JavaScript code to make SOAP calls easier to maintain and easier to move to REST when you decide it’s time.

Remember that older car I drive? Well, over the years, I’ve had work done on it a few times to buff out scratches or pull out dents. In this article, I’ll talk about how you can get your code in better shape to prepare for REST down the road.

Patterns to improve SPServices-based code; moving SPServices to REST


Photo Credit: “Erie LaSalle Body Shop & Car Care Center” by Thomas Hawk on Flickr https://flic.kr/p/8bA2yH

Over the last year or so, I’ve started to follow some new patterns in writing my JavaScript code. The ideas came primarily from a smart client of mine named Doug Coutts. He came to me for some help setting up some data access methods using SPServices in a SharePoint 2010 environment.

When Doug first got in touch, he sent me an extremely well thought out spreadsheet containing the capabilities he was after. The spreadsheet had about 25 different data access methods in it. Doug had a nice data model built using SharePoint lists as the repositories. Each list would have a set of specific data access methods based on the expected functionality in the application. At first I was simply going to build out a decent set of those methods to get him started, but I ended up building most of them in the original spreadsheet plus quite a few more we figured out we’d need as we went along in the development process.

So why is this all important to you, gentle reader? Well, Doug’s patterns, along with others I’ve learned from great articles from Scot Hillier and others, are almost exactly what I’m recommending for you to move to in order to start preparing for switching from the SOAP Web Services to REST over time.

As I’ll admit to anyone who listens, I’m sort of a hack. I write code that works, and it usually works very well, but I’m not much of a patterns and practices kind of guy. Because of the types of projects I work on with SharePoint, I’m not usually part of some big development team. Instead, I tend to end up building smaller, self-contained “application-lets” if you will, which don’t require more hands than mine.

But over the last few years as the SharePoint team has been moving more and more toward client-side code and I’ve picked apart how they are doing it (Let’s continue to improve that documentation for the client-side code, guys!), I’ve learned more and better ways to do what I’ve already been doing. Even inside SPServices, I make continuous improvements. On some levels, that’s a bad idea, as it can mean that I end up breaking things that worked just fine before – we call those regressions in the software biz – or I simply do work for more academic purposes. However, each time I do a new release of SPServices, I’m less embarrassed about what’s inside and it simply works better.

The new patterns I’m suggesting here will be sort of like that, but with two clear purposes:

  • Improving your code base to make your code as it works today easier to maintain
  • Getting you more ready to move components of your code to REST when you decide it is time

Some of you will already be doing these things, but having seen a lot of the bits and pieces of how you all use SPServices in the wild and in the Codeplex Discussions, I know that many of you don’t.

Here’s the basic idea. While SPServices is already an abstraction on the SOAP Web Services, I suggest that you add yet another abstraction layer in your SPServices-based code.

Say we have a list called Tasks, basically the same as the out-of-the-box Tasks list that shows up in SharePoint Team Sites and elsewhere. Tasks themselves aren’t very complicated: we have a Title, a few descriptive fields that tell us some things about each task, and some dates that tell us when we need to do the task. However, there may be 5 or 6 different ways we want to interact with the list. None of them will shock you, but they are things like:

  • Create a new Task
  • Edit an existing Task
  • Delete a Task
  • Get all of the Tasks created by a specific person
  • Get all the Tasks assigned to a specific person
  • etc.

Nothing earth-shattering, right? Just the basic CRUD (Create, Read, Update, Delete) operations, and a few other methods that are only a bit more complicated. But we may need to call some of the functions in multiple places in the application. You may see where I’m heading here: when there is a strong possibility that we will use a given method in more than one place in our code, it pays to abstract that method out and not repeat it.

Not only do we have some unique script (may I finally call it code?) that drives each individual page in the application (in this case each of the dozen or so pages has its own unique set of business logic), we also have a separate script file that contains all of the data access methods, making them available for reuse across the page logic. This idea is known as abstraction, and with abstraction and modularity we can make our development chores a lot easier. We’re usingKnockoutJS in our specific project, but that doesn’t really matter much when it comes to this part of the pattern.

Overall, the data access script file looks a lot like the way I’ve structured SPServices itself. The structure looks something like this:

  • Surrounding function
  • Defaults
  • A Namespace per data source
  • Individual data access functions for the data source

Inside the data access module, I’ve created each data access method inside a single namespace for the application – let’s call it TasksApp. When we talk about namespaces in JavaScript, we really mean complex objects that limit our impact on other people’s code. Each list – or data source – has its own namespace inside TasksApp, and then we have the series of functions. They look something like this:

  • TasksApp.Tasks.create
  • TasksApp.Tasks.save
  • TasksApp.Tasks.delete
  • etc.

Here’s what the TasksApp.Tasks.delete function’s code looks like. It’s a nice one to show because it’s so simple.

$.fn.TasksApp.Tasks.delete = function (options) {

    var opt = $.extend({}, {
        task: null
    }, $.fn.TasksApp.defaults, options);

    var result = $.Deferred();
    var p = $.Deferred();

    var p = $().SPServices({
        operation: "UpdateListItems",
        listName: opt.tasksList,
        batchCmd: "Delete",
        ID: opt.task.ID
    });

    p.done(function () {

        var errorCode = $(p.responseXML).find("Result > ErrorCode").text();

        if (errorCode === "0x00000000") {
            // Success

            result.resolveWith(opt.task.ID);
        } else {
            // Something went wrong
            result.resolveWith(-1);
        }

    });

    return result.promise();

}; // End $.fn.TasksApp.Tasks.delete

What this does for me is give me one function I can call from anywhere in the application when I want to delete a task. I simply pass a task object (this is defined elsewhere, but consists of the task in an object like { ID: 12, Title: ‘This is the title’, AssignedTo: ‘Anderson, Marc’, … }) into the function, and I know that it will be deleted. I’m using promises internally in the function, and I’m also returning a promise that will eventually tell me whether the call to the function succeeds or not. I have nicely chunked out functions with mnemonic names that we can call from anywhere in the application without any duplication of the actual calls to the SOAP Web Services.Notice what else this does for me: I have encapsulated the Web Services call. All I need to know in my application is that the function will delete a task, wherever it lives. In this case, it’s in a SharePoint list, but we could decide to store our tasks in SQL or any other storage location later.

Even more importantly, I can decide at any point that I want to start deleting tasks via REST. Since I know what I need to pass into the function and what it does, I don’t need to care how it does its thing. Because I have a nice encapsulation, I can decide I want to convert one particular function, all the delete functions, or all of the functions in the entire application, but it’s up to me what pace I decide to take.

Let’s say I decide to switch this one TasksApp.Tasks.delete function to REST. It simply becomes this:

$.fn.TasksApp.Tasks.delete = function (options) {

    var opt = $.extend({}, {
        task: null
    }, $.fn.TasksApp.defaults, options);

    var result = $.Deferred();
    var p = $.Deferred();

    var p = $.ajax({
        url: "http://siteurl/_api/web/lists/GetByTitle('Tasks')/items(" + opt.task.ID + ")",
        method: "POST",
        headers: {
            Authorization: "Bearer " + accessToken
    X- RequestDigest: form digest value
    "IF-MATCH": etag or "*"
    "X-HTTP-Method": "DELETE"}
    });

p.done(function () {

    var errorCode = $(p.responseXML).find("Result > ErrorCode").text();

    if (errorCode === "0x00000000") {
        // Success

        result.resolveWith(opt.ID);
    } else {
        // Something went wrong
        result.resolveWith(-1);
    }

});

return result.promise();
    
    }; // End $.fn.TasksApp.tasks.delete

Any code that calls the function TasksApp.Tasks.delete function simply expects to receive a promise back that will be resolved either with the ID of the task that was deleted or -1 if there was a problem. The calling code doesn’t care how it happens, just that it will be informed about the outcome.

About two-thirds of the way through the development cycle in SharePoint 2010, Doug asked me what it would take to upgrade everything to SharePoint 2013. I thought a minute and said, “Copying the files into the SharePoint 2013 environment.” And you know what? It was just about that easy. Take that, managed code developers!

We took a backup of the development Site Collection and restored it into the 2013 environment. Then I simply copied and pasted the script, CSS and HTML files we had built into the 2013 environment. The “conversion” was a big copy of the files from a Document Library using SharePoint Designer 2010 and a paste into a Document Library using SharePoint Designer 2013.

Caveat: there was *one* thing I needed to fix. While “protocol-less” script references are considered good practice, SharePoint Designer doesn’t deal well with them and tries to fix them up; breaking them in the process. So I did have to repair those script reference hrefs, but I didn’t need to rewrite a single line of code!

At that point we were in SharePoint 2013 and I was calling the SOAP Web Services with SPServices. Shouldn’t I have switched to REST calls right away? We decided not to tackle that then because we wanted to finish the application and get it released. I’ve pushed to do some replacement in subsequent versions, and we’ve done it where it makes sense. It’s not a very big deal because we have the discrete data access methods in place. In fact, we have replaced only the methods we were using during updates with REST calls and can tackle others later. We may continue to decide which calls fall into each category simply by what the REST services support or we may decide based on level of effort, but the level of effort won’t be high, I guarantee it.

This article was also published on IT Unity on April 6, 2015. Visit the post there to read additional comments.
Series Navigation<< Moving from SPServices to REST, Part 1: IntroductionMoving from SPServices to REST, Part 3: JSON vs. XML >>

Similar Posts

4 Comments

  1. This is awesome. Anyone have a good resource for understanding promises and $.deferred? I need to kick up the comprehension a notch on that.

  2. What I have done in my library is pass the Web service type as an option (api:”SOAP” or api:”client” in the options object). This way, the switch can be done anytime by each user.

Leave a Reply

Your email address will not be published. Required fields are marked *

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