Archive | May 2016

MS Dynamics 2016 new Web API: So, what do we do now?

As you may already know, MS Dynamics CRM 2016 comes with a new API for web development, and even a new endpoint has been implemented. So, for all of us who had developed resources using the old API, does that mean that we need to rush into a massive refactoring crusade? Well, not for the moment since the old (CRM2011) endpoint is still available, and hopefully will be supported for the near future.

This article won’t be a summary of the new features in the new API, you can find plenty of material on the topic. A good starter can be found here. However, I wanted to provide a side to side comparison of the main syntactical differences between the new and old APIs, for which I have created a very simple web-resource. As a bonus, I made this web-resource backwards-compatible which proves useful if you have to maintain the same code-base for multiple versions of Dynamics CRM.

The sample shown next is for a very simple HTML web resource that displays all the active system views in the organization, sorted in alphabetical order.


  1. As I mentioned before, the endpoints are different; however, the server url remains the same.
    Old Suffix: "/XRMServices/2011/OrganizationData.svc/"
    New Suffix: "/api/data/v8.0/"
  2. Entity and field names no longer need to match the exact casing defined in the schema names. Also, for entities we no longer need to use a combination “Entity”+”Set”, we just need to use the plural name for the entity.
    Old Entity Format: SavedQuerySet
    New Entity Format: savedqueries
  3. The syntax for filters on complex data types like EntityReference or OptionSet is different.
    Old format: filter=StateCode/Value eq 0
    New format: filter=statecode eq 0
  4. The old page size is 50, whereas, the Web API allows you to set the page size, which in turn can improve performance by reducing the number of trips to SQL server.
    New syntax for setting page size: setRequestHeader("Prefer", "odata.maxpagesize=100")
  5. The syntax to verify whether there’s more data to fetch is also slightly different.
    Old syntax: __next != null
    New syntax: @odata.nextLink != null

Final thoughts

If you are implementing web resources for Dynamics CRM 2016, you can still use the old API; however, it’s definitely a good idea to start using the new web API since the functionality offered is richer and brings the opportunity to develop components that perform faster.

HTML markup:

 <title>Sample: Show All System Views</title>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 < script src="ClientGlobalContext.js.aspx"></script>
 < script src="bpi_testwebapi.js" type="text/javascript"></script>
<body style="word-wrap: break-word;">
 < div style="margin-right: 8px; margin-left: 13px;">
 <ol id="systemViews">
 < script type="text/javascript">Setup();</script>

Supporting JavaScript:

function Setup() {
 var filterExpression = "savedqueries?$select=name,savedqueryid&$filter=statecode eq 0&$orderby=name asc";
 var url = getClientUrl() + "/api/data/v8.0/";
 var qry = url + filterExpression;

function Setup_Pre2016() {
 var filterExpression = "SavedQuerySet?$select=Name,SavedQueryId&$filter=StateCode/Value eq 0&$orderby=Name asc";
 var url = getClientUrl() + "/XRMServices/2011/OrganizationData.svc/";
 var qry = url + filterExpression;


function RetieveData(url) {
 var req = new XMLHttpRequest()"GET", encodeURI(url), true);
 req.setRequestHeader("Accept", "application/json");
 req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
 req.setRequestHeader("OData-MaxVersion", "4.0");
 req.setRequestHeader("OData-Version", "4.0");
 req.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");
 req.setRequestHeader("Prefer", "odata.maxpagesize=100");
 req.onreadystatechange = function () {
 if (this.readyState == 4 /* complete */) {
 req.onreadystatechange = null;
 switch (this.status) {
 case 200:
 var result = JSON.parse(this.response);
 // browse thru all results and build html
 var values = result.value;// type array 

 for (i = 0; i < values.length; i++) {
 if (result["@odata.nextLink"] != null) {
 case 503:
 // service not available - we need to call the pre-2016 endpoint
 var error = JSON.parse(this.response).error;


function RetieveData_Pre2016(url) {
 var retrieveReq = new XMLHttpRequest();"GET", url, false);
 retrieveReq.setRequestHeader("Accept", "application/json");
 retrieveReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
 retrieveReq.onreadystatechange = function () {
 if (retrieveReq.readyState == 4) {
 // complete
 var retrieved = JSON.parse(retrieveReq.responseText).d;
 for (var i = 0; i < retrieved.results.length; i++) {

 // Check if there are more records to fetch
 if (retrieved.__next != null) {

function getClientUrl() {
 //Get the organization URL
 if (typeof GetGlobalContext == "function" &&
 typeof GetGlobalContext().getClientUrl == "function") {
 return GetGlobalContext().getClientUrl();
 else {
 //If GetGlobalContext is not defined check for Xrm.Page.context;
 if (typeof Xrm != "undefined" &&
 typeof Xrm.Page != "undefined" &&
 typeof Xrm.Page.context != "undefined" &&
 typeof Xrm.Page.context.getClientUrl == "function") {
 try {
 return Xrm.Page.context.getClientUrl();
 } catch (e) {
 throw new Error("Xrm.Page.context.getClientUrl is not available.");
 else { throw new Error("Context is not available."); }

function addNewView(viewName) {
 var newLine = document.createElement("LI");