Microsoft MVP Logo

This post is part of a series I've written on using the popular Breeze JavaScript library with the SharePoint 2013 REST API. Here's a list of all the posts in this series:

There's a good amount of code snippets in this post. Typically code formatting doesn't render great in RSS readers so check out the post on my blog for a well formatted version.

So far in this series of posts I've introduced you to the Breeze library (part 1) and some challenges we have in using it OOTB (part 2). In this post I want to explain how you can use it. This breaks down into a few different tasks:

  • Acquire the Breeze Library
  • Acquire the Breeze SharePoint Data Service
  • Configure Breeze to work with SharePoint
  • Define your Entity Metadata Model
  • Use It!

As I walk through each of these steps, I'm trying to keep this as simple as possible. I'm doing everything in one file to make exploring the code as easy as possible, but in the real world I never do this. I prefer many JavaScript files and let each one serve a specific purpose, making the code more maintainable. But here I've done it all in one file. You can download the sample app I used to create this post from this public GitHub project I created: BreezeSP2013Sample. Just grab the code, open it in Visual Studio 2012/2013, change the Site URL property for the project to point to your SharePoint developer site (on-prem or Office 365) and hit F5 to see how it works! 

Let's dive in so you can see what's involved...

Acquire the Breeze Library

You first need to get the Breeze client-side library. This is provided as a NuGet package: Breeze.Client. For this you need the latest version (v1.4.9) which came out in late February 2014. This is by far the easiest step because you can skip it! You'll see why in a moment...

Acquire the Breeze SharePoint Data Service

Next, you need to get the Breeze SharePoint Data Service adapter. This is what Ward Bell, one of the brains behind Breeze, and I have been working on. It's in a easy to grab NuGet package: Breeze.DataService.SharePoint. Best part is it already includes the Breeze client library package which is why you could ignore the previous step. It also includes a few other dependencies.

Once you've done this, add the necessary references to your HTML page, as shown here:

<script type="text/javascript" src="../Scripts/q.js"></script>
<script type="text/javascript" src="../Scripts/breeze.debug.js"></script>
<script type="text/javascript" src="../Scripts/breeze.labs.dataservice.abstractrest.js"></script>
<script type="text/javascript" src="../Scripts/breeze.labs.dataservice.sharepoint.js"></script>
<script type="text/javascript" src="../Scripts/breeze.metadata-helper.js"></script>

... just the HTML page with references

Configure Breeze to work with SharePoint

After you get everything, you then need to configure Breeze. This involves first initializing Breeze to use the new SharePoint data service adapter using the breeze.config.initializeAdapterInstance() function:

// configure breeze to use SharePoint OData service
var dsAdapter = breeze.config.initializeAdapterInstance('dataService', 'SharePointOData', true);

Then you need to tell Breeze how to get the security validation token, also known as the form digest, security digest, or the request digest. This is needed when you want to do an HTTP POST or DELETE against the SharePoint REST API. For this I'll simply use the hidden form field added to the page:

// tell breeze how to get the security validation token for 
//  HTTP POST & DELETE calls
dsAdapter.getRequestDigest = function () {
  return jQuery('#__REQUESTDIGEST').val();
};

OK... everything is all configured. Next up, tell Breeze about our entities.

Define your Entity Metadata Model

In this step you will build the metadata that tells Breeze about the list we want to work with. In my sample app I created a simple Contacts list in the app and to keep things simple, I only want to deal with the first and last names as well as the email address.

Writing the metadata can involve a good amount of code as you would expect. However over in the Breeze Labs there is a slick helper utility library called the Breeze Metadata Helper that helps quite a bit. I'll add this via an easy to use NuGet package: Breeze.Metadata.Helper.

Then I'll get a reference to the metadata helper and create an alias named addType() that will add the new entity type to the metadata store for me as well as setup a default select statement so I don't have to deal with that in my code:

// create a new breeze metadata store
metadataStore = new breeze.MetadataStore();

// setup a helper to create entities
var namespace = '';
var helper = new breeze.config.MetadataHelper(
                    namespace, 
                    breeze.AutoGeneratedKeyType.Identity);
// define a new function that uses the helper to
//  1) create a new entity type in the metadata store
//  2) create a default select so we don't have to create the
//    OData $select each time
var addType = function (typeDef) {
  var entityType = helper.addTypeToStore(metadataStore, typeDef);
  _addDefaultSelect(entityType);
  return entityType;
};

// add 'defaultSelect' custom metadata that selects for all 
//  mapped data properties could be used by SharePoint dataservice 
//  adapter to exclude unwanted property data in query payload
function _addDefaultSelect(type) {
  var custom = type.custom;
  // bail out if defined by hand already
  if (custom && custom.defaultSelect != null) { return; }

  var select = [];
  type.dataProperties.forEach(function (prop) {
    if (!prop.isUnmapped) { select.push(prop.name); }
  });
  if (select.length) {
    if (!custom) { type.custom = custom = {}; }
    custom.defaultSelect = select.join(',');
  }
  return type;
}

Last but not least, I'm going to create an entity that maps to the contact list... take notice that I am not getting all fields from the contact list, just the ones I'm interested in. Oh, check out the email property... Breeze won't let me save it unless it passes Breeze's RegEx email address validation!:

// create entity for contacts
addType({
  name: 'Contact',
  defaultResourceName: 'getbytitle(\'Contacts\')/items',
  dataProperties: {
    Id: { type: breeze.DataType.Int32 },
    FirstName: { nullable: false },
    Title: { nullable: false },
    Email: {
      nullable: false,
      validators: [breeze.Validator.emailAddress()]
    }
  }
});

There are a few properties to take note of:

  • name: this is the name that we'll use to reference our entity if we need to throughout our app
  • defaultResourceName: this is the endpoint for the list... not everything is listed here in the URL because as you'll see, we'll define the base part to the REST endpoint for the site in a moment
  • dataProperties: collection of the fields we want to get from SharePoint; notice some of these also include validation statements to enforce some data integrity

So with that, Breeze has been added to the page, configured and the metadata has been defined. Let's now use it!

For more information on working with Breeze and SharePoint, check out my upcoming new Pluralsight course, coming soon in March 2014 now available: Building SharePoint Apps as Single Page Apps with AngularJS. Module 5 is all about using Breeze with SharePoint.

Using Breeze with SharePoint 2013's REST API

Before we start making calls, let's first get an instance of something called the EntityManager. This is what will make the calls to the server for us. Part of this is also defining the data service, telling the SharePoint data service adapter to not request metadata from SharePoint's REST API and what the root URL is in the REST API:

// init breeze for queries
function initBreeze() {
  // get reference to contact entity type
  contactType = metadataStore.getEntityType('Contact');

  // create the data service
  var dataService = new breeze.DataService({
    // tell breeze the root REST endpoint to use
    //  since we only are using lists, point to that
    serviceName: _spPageContextInfo.webAbsoluteUrl + '/_api/web/lists/',
    // tell breeze not to interrogate sharepoint for it's
    //  massive OData $metadata response... we created it manually
    hasServerMetadata: false
  });

  // create an instance of the entity manager
  entityManager = new breeze.EntityManager({
    dataService: dataService,
    // tell breeze where the metadata is
    metadataStore: metadataStore
  });
}

Now let's see how to get data... for this I'll fetch all the results from the contact list, using Breeze:

breeze.EntityQuery
    .from(contactType.defaultResourceName)
    .using(entityManager)
    .execute()
    .then(function (response) {
      var results = response.results;
      // write results > div
      if (results && results.length) {
        var message = '';
        for (var index = 0; index < results.length; index++) {
          message += results[index].FirstName 
            + ' ' + results[index].Title 
            + ' (' + results[index].Email + ')
'; } jQuery("#results").html(message); } });

And if we wanted to get only one item, here's how you do a filter:

breeze.EntityQuery
    .from(contactType.defaultResourceName)
    .using(entityManager)
    .execute()
    .where('Id', 'eq', 1)
    .then(function (data) {
      var message = data.entity.FirstName 
          + ' ' + data.entity.Title 
          + ' (' + data.entity.Email + ')
'; jQuery("#results").html(message); });

What about updating items? Well all you do is get the item, make some changes and just call save!

// get the first item
  var promise = breeze.EntityQuery
    .from(contactType.defaultResourceName)
    .using(entityManager)
    .execute()
    .where('Id', 'eq', 1)
    .then(function (data) {
      return data.entity;
    });

  // update the first item
  promise.then(function (contact) {
    contact.Title = 'NewName';
    entityManager.saveChanges().then(function () {
      jQuery("#results").html('saved first item in list');
    });
  });

And deleting an item? This is similar to an update item:

// deletes the last item in the list
function deleteItem() {
  // delete the last item in the list
  breeze.EntityQuery
    .from(contactType.defaultResourceName)
    .using(entityManager)
    .execute()
    .then(function (response) {
      var results = response.results;
      var lastContact = results[results.length - 1];
      lastContact.entityAspect.setDeleted();
      entityManager.saveChanges()
        .then(function () {
          jQuery("#results").html('last item in list deleted');
        });
    });
}

And a very cool piece to this. Once you get items back from SharePoint which are stored in the Breeze local cache, you can re-fetch them at a later time without going to the server. The following call is effectively telling Breeze “get a contact with ID=1 from cache, but if you don't find it, then go to the server to find it:”

// try to get a single item from the cache, then revert to server
  entityManager.fetchEntityByKey('Contact', 1, true)
  .then(function (data) {
    var message = data.entity.FirstName 
        + ' ' + data.entity.Title 
        + ' (' + data.entity.Email + ')
'; message += 'pulled from: ' + (data.fromCache ? 'cache' : 'server'); jQuery("#results").html(message); });

Pretty slick huh? Again, remember what I said at the beginning of the post. I know you think “that was a lot of work at the start to configure and set it up.” I would agree with that to a point, but most of it is boilerplate code... everything except the creation of the metadata would be the same from one project to another.

Again... you can download the sample app I used to create this post from this public GitHub project I created: BreezeSP2013Sample. Just grab the code, open it in Visual Studio 2012/2013, change the Site URL property for the project to point to your SharePoint developer site (on-prem or Office 365) and hit F5 to see how it works! Hopefully you'll find this as cool as I do... I can't imagine working with SharePoint's REST API without Breeze ever again! It just makes working with SharePoint's REST API... well... a Breeze!

Comments powered by Disqus