DynamicsWebApi for Microsoft Dynamics CRM Web API

DynamicsWebApi is a Microsoft Dynamics CRM Web API helper library written in JavaScript.
It is compatible with: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online.
Libraries for browsers can be found in dist folder.
Any suggestions are welcome!
Table of Contents
Getting Started
DynamicsWebApi as a Dynamics 365 web resource
In order to use DynamicsWebApi inside Dynamics 365 you need to download a browser version of the library, it can be found in dist folder.
Upload a script as a JavaScript Web Resource, place on the entity or refer to it in your HTML Web Resource and then initialize the main object:
var dynamicsWebApi = new DynamicsWebApi();
dynamicsWebApi.executeUnboundFunction("WhoAmI").then(function (response) {
Xrm.Utility.alertDialog('Hello Dynamics 365! My id is: ' + response.UserId);
}).catch(function(error){
console.log(error.message);
});
DynamicsWebApi for Node.js
DynamicsWebApi can be used as Node.js module to access Dynamics 365 Web API using OAuth.
First of all, install a package from NPM:
npm install dynamics-web-api --save
Then include it in your file:
var DynamicsWebApi = require('dynamics-web-api');
At this moment DynamicsWebApi does not fetch authorization tokens, so you will need to acquire OAuth token in your code and pass it to the DynamicsWebApi.
Token can be aquired using ADAL for Node.js or you can write your own functionality, as it is described here.
Here is a sample using adal-node
:
var DynamicsWebApi = require('dynamics-web-api');
var AuthenticationContext = require('adal-node').AuthenticationContext;
var authorityUrl = 'https://login.windows.net/00000000-0000-0000-0000-000000000011/oauth2/token';
var resource = 'https://myorg.crm.dynamics.com';
var clientId = '00000000-0000-0000-0000-000000000001';
var username = 'crm-user-name';
var password = 'crm-user-password';
var adalContext = new AuthenticationContext(authorityUrl);
function acquireToken(dynamicsWebApiCallback){
function adalCallback(error, token) {
if (!error){
dynamicsWebApiCallback(token);
}
else{
console.log('Token has not been retrieved. Error: ' + error.stack);
}
}
adalContext.acquireTokenWithUsernamePassword(resource, username, password, clientId, adalCallback);
}
var dynamicsWebApi = new DynamicsWebApi({
webApiUrl: 'https:/myorg.api.crm.dynamics.com/api/data/v8.2/',
onTokenRefresh: acquireToken
});
dynamicsWebApi.executeUnboundFunction("WhoAmI").then(function (response) {
console.log('Hello Dynamics 365! My id is: ' + response.UserId);
}).catch(function(error){
console.log(error.message);
});
Configuration
To initialize a new instance of DynamicsWebApi with a configuration object, please use the following code:
var dynamicsWebApi = new DynamicsWebApi({ webApiVersion: "8.2" });
You can set a configuration dynamically if needed:
dynamicsWebApi.setConfig({ webApiVersion: "8.2" });
Configuration Parameters
Property Name | Type | Description |
---|
impersonate | String | A String representing the GUID value for the Dynamics 365 system user id. Impersonates the user. |
includeAnnotations | String | Defaults Prefer header with value "odata.include-annotations=" and the specified annotation. Annotations provide additional information about lookups, options sets and other complex attribute types. |
maxPageSize | Number | Defaults the odata.maxpagesize preference. Use to set the number of entities returned in the response. |
onTokenRefresh | Function | A callback function that triggered when DynamicsWebApi requests a new OAuth token. (At this moment it is done before each call to Dynamics 365, as recommended by Microsoft). |
returnRepresentation | Boolean | Defaults Prefer header with value "return=representation". Use this property to return just created or updated entity in a single request. |
webApiUrl | String | A complete URL string to Web API. Example of the URL: "https:/myorg.api.crm.dynamics.com/api/data/v8.2/". If it is specified then webApiVersion property will not be used even if it is not empty. |
webApiVersion | String | Version of the Web API. Default version is "8.0". |
Configuration property webApiVersion
is required only when DynamicsWebApi used inside of CRM.
Property webApiUrl
is required when DynamicsWebApi used externally.
If both configuration properties set webApiUrl
will have the highest priority than webApiVersion
, so the last one will be skipped.
Request Examples
DynamicsWebApi supports Basic and Advanced calls to Web API.
Basic calls can be made by using functions with most commonly used input parameters. They are most convenient for simple operations as they do
not provide all possible ways of interaction with CRM Web API (for example, conditional retrievals
are not supported in basic functions).
Basic functions are: create
, update
, upsert
, deleteRecord
, retrieve
, retrieveMultiple
, count
, executeFetchXml
,
associate
, disassociate
, associateSingleValued
, disassociateSingleValued
, executeBoundFunction
, executeUnboundFunction
,
executeBoundAction
, executeUnboundAction
.
Advanced functions have a suffix Request
added to the end of the applicable operation.
Most of the functions have a single input parameter which is a request
object.
The following table describes all properties that are accepted in this object. Important! Not all operaions accept all properties and if you by mistake specified
an invalid property you will receive either an error saying that the request is invalid or the response will not have expected results.
Property Name | Type | Operation(s) Supported | Description |
---|
collection | String | All | The name of the Entity Collection, for example, for account use accounts , opportunity - opportunities and etc. |
count | Boolean | retrieveMultipleRequest | Boolean that sets the $count system query option with a value of true to include a count of entities that match the filter criteria up to 5000 (per page). Do not use $top with $count! |
entity | Object | updateRequest , upsertRequest | A JavaScript object with properties corresponding to the logical name of entity attributes (exceptions are lookups and single-valued navigation properties). |
expand | Array | retrieveRequest , updateRequest , upsertRequest | An array of Expand Objects (described below the table) representing the $expand OData System Query Option value to control which related records are also returned. |
filter | String | retrieveRequest , retrieveMultipleRequest | Use the $filter system query option to set criteria for which entities will be returned. |
id | String | retrieveRequest , updateRequest , upsertRequest , deleteRequest | A String representing the GUID value for the record. |
ifmatch | String | retrieveRequest , updateRequest , upsertRequest , deleteRequest | Sets If-Match header value that enables to use conditional retrieval or optimistic concurrency in applicable requests. More info. |
ifnonematch | String | retrieveRequest , upsertRequest | Sets If-None-Match header value that enables to use conditional retrieval in applicable requests. More info. |
impersonate | String | All | A String representing the GUID value for the Dynamics 365 system user id. Impersonates the user. |
includeAnnotations | String | retrieveRequest , retrieveMultipleRequest , updateRequest , upsertRequest | Sets Prefer header with value "odata.include-annotations=" and the specified annotation. Annotations provide additional information about lookups, options sets and other complex attribute types. |
maxPageSize | Number | retrieveMultipleRequest | Sets the odata.maxpagesize preference value to request the number of entities returned in the response. |
navigationProperty | String | retrieveRequest | A String representing the name of a single-valued navigation property. Useful when needed to retrieve information about a related record in a single request. |
orderBy | Array | retrieveMultipleRequest | An Array (of Strings) representing the order in which items are returned using the $orderby system query option. Use the asc or desc suffix to specify ascending or descending order respectively. The default is ascending if the suffix isn't applied. |
returnRepresentation | Boolean | updateRequest , upsertRequest | Sets Prefer header request with value "return=representation". Use this property to return just created or updated entity in a single request. |
savedQuery | String | retrieveRequest | A String representing the GUID value of the saved query. |
select | Array | retrieveRequest , retrieveMultipleRequest , updateRequest , upsertRequest | An Array (of Strings) representing the $select OData System Query Option to control which attributes will be returned. |
token | String | All | Authorization Token. If set, onTokenRefresh will not be called. |
top | Number | retrieveMultipleRequest | Limit the number of results returned by using the $top system query option. Do not use $top with $count! |
userQuery | String | retrieveRequest | A String representing the GUID value of the user query. |
Basic and Advanced functions are also have differences in expand
parameters. For Basic ones this parameter is a type of String
while request.expand property is an Array of Expand Objects for Advanced operations. The following table describes Expand Object properties:
Property Name | Type | Description |
---|
filter | String | Use the $filter system query option to set criteria for which related entities will be returned. |
orderBy | Array | An Array (of Strings) representing the order in which related items are returned using the $orderby system query option. Use the asc or desc suffix to specify ascending or descending order respectively. The default is ascending if the suffix isn't applied. |
property | String | A name of a single-valued navigation property which needs to be expanded. |
select | Array | An Array (of Strings) representing the $select OData System Query Option to control which attributes will be returned. |
top | Number | Limit the number of results returned by using the $top system query option. |
According to CRM developers (here and here
$expand does not work for retrieveMultiple requests which is claimed as a bug of CRM Web API.
As well as multi-level expands are not implemented yet. This situation may be changed with the future updates in the platform. Please look for the news!
For complex requests to Web API with multi-level expands use executeFetchXml
function.
Create a record
var lead = {
subject: "Test WebAPI",
firstname: "Test",
lastname: "WebAPI",
jobtitle: "Title"
};
dynamicsWebApi.create(lead, "leads").then(function (id) {
}).catch(function (error) {
})
If you need to return just created entity record, add "return=representation" parameter:
var lead = {
subject: "Test WebAPI",
firstname: "Test",
lastname: "WebAPI",
jobtitle: "Title"
};
dynamicsWebApi.create(lead, "leads", ["return=representation"]).then(function (record) {
var subject = record.subject;
}).catch(function (error) {
})
Also you can include attribute annotations:
dynamicsWebApi.create(lead, "leads", ["return=representation", "odata.include-annotations=*"])
dynamicsWebApi.create(lead, "leads", "return=representation,odata.include-annotations=*")
dynamicsWebApi.create(lead, "leads", ["return=representation", "odata.include-annotations=*"], ["subject"])
Update a record
Basic
var leadId = '7d577253-3ef0-4a0a-bb7f-8335c2596e70';
var lead = {
subject: "Test update",
jobtitle: "Developer"
}
dynamicsWebApi.update(leadId, "leads", lead).then(function () {
})
.catch(function (error) {
});
Advanced using Request Object
var request = {
id: '7d577253-3ef0-4a0a-bb7f-8335c2596e70',
collection: "leads",
entity: {
subject: "Test update",
jobtitle: "Developer"
},
returnRepresentation: true,
select: ["fullname"]
};
dynamicsWebApi.updateRequest(request).then(function (response) {
var fullname = response.fullname;
})
.catch(function (error) {
});
Update a single property value
var leadId = '7d577253-3ef0-4a0a-bb7f-8335c2596e70';
var keyValuePair = { subject: "Update Single" };
dynamicsWebApi.updateSingleProperty(leadId, "leads", keyValuePair).then(function () {
})
.catch(function (error) {
});
Upsert a record
Basic
var leadId = '7d577253-3ef0-4a0a-bb7f-8335c2596e70';
var lead = {
subject: "Test Upsert"
};
dynamicsWebApi.upsert(leadId, "leads", lead).then(function (id) {
})
.catch(function (error) {
});
Advanced using Request Object
var leadId = '7d577253-3ef0-4a0a-bb7f-8335c2596e70';
var request = {
id: leadId,
collection: "leads",
returnRepresentation: true,
select: ["fullname"],
entity: {
subject: "Test upsert"
},
ifnonematch: "*"
};
dynamicsWebApi.upsertRequest(request).then(function (record) {
if (record != null) {
}
else {
}
})
.catch(function (error) {
});
Delete a record
Basic
var leadId = '7d577253-3ef0-4a0a-bb7f-8335c2596e70';
dynamicsWebApi.deleteRecord(leadId, "leads").then(function () {
})
.catch(function (error) {
});
Advanced using Request Object
var request = {
id: recordId,
collection: "leads",
ifmatch: 'W/"470867"'
}
dynamicsWebApi.deleteRequest(request).then(function (isDeleted) {
if (isDeleted){
}
else{
}
})
.catch(function (error) {
});
Delete a single property value
var leadId = '7d577253-3ef0-4a0a-bb7f-8335c2596e70';
dynamicsWebApi.deleteRecord(leadId, "leads", "subject").then(function () {
})
.catch(function (error) {
});
Retrieve a record
Basic
var leadId = '7d577253-3ef0-4a0a-bb7f-8335c2596e70';
dynamicsWebApi.retrieve(leadid, "leads", ["fullname", "subject"]).then(function (record) {
})
.catch(function (error) {
});
Advanced using Request Object
var request = {
id: '7d577253-3ef0-4a0a-bb7f-8335c2596e70',
collection: "leads",
select: ["fullname", "subject"],
ifnonematch: 'W/"468026"',
includeAnnotations: "OData.Community.Display.V1.FormattedValue"
};
dynamicsWebApi.retrieveRequest(request).then(function (record) {
})
.catch(function (error) {
});
Retrieve a reference to related record using a single-valued navigation property
It is possible to retrieve a reference to the related entity (it works both in Basic and Advanced requests): select: ["ownerid/$ref"]
. The parameter
must be the only one, it must be the name of a single-valued navigation property
and it must have a suffix /$ref
attached to it. Example:
var leadId = '7d577253-3ef0-4a0a-bb7f-8335c2596e70';
dynamicsWebApi.retrieve(leadid, "leads", ["ownerid/$ref"]).then(function (reference) {
var ownerId = reference.id;
var collectionName = reference.collection;
})
Retrieve a related record data using a single-valued navigation property
In order to retrieve a related record by a signle-valued navigation property you need to add a prefix "/" to the first element in a select
array:
select: ["/ownerid", "fullname"]
. The first element must be the name of a single-valued navigation property
and it must contain a prefix "/"; all other elements in a select
array will represent attributes of the related entity. Examples:
var recordId = '7d577253-3ef0-4a0a-bb7f-8335c2596e70';
dynamicsWebApi.retrieve(recordId, "new_tests", ["/new_ParentLead", "fullname", "subject"])
.then(function (leadRecord) {
var fullname = leadRecord.fullname;
})
In advanced request you have a choice to specify a request.navigationProperty
or use it in the same way as for the Basic function.
var request = {
id: recordId,
collection: "new_tests",
navigationProperty: "new_ParentLead",
select: ["fullname", "subject"]
}
request = {
id: recordId,
collection: "new_tests",
select: ["/new_ParentLead", "fullname", "subject"]
}
dynamicsWebApi.retrieveRequest(request).then(function (leadRecord) {
var fullname = leadRecord.fullname;
})
Retrieve multiple records
Basic
dynamicsWebApi.retrieveMultiple("leads", ["fullname", "subject"], "statecode eq 0").then(function (records) {
})
.catch(function (error) {
});
Advanced using Request Object
var request = {
collection: "leads",
select: ["fullname", "subject"],
filter: "statecode eq 0",
maxPageSize: 5,
count: true
};
dynamicsWebApi.retrieveMultipleRequest(request).then(function (response) {
var count = response.oDataCount;
var nextLink = response.oDataNextLink;
var records = response.value;
})
.catch(function (error){
});
Count
It is possible to count records separately from RetrieveMultiple call. In order to do that use the following snippet:
IMPORTANT! The count value does not represent the total number of entities in the system. It is limited by the maximum number of entities that can be returned.
For now please use dynamicsWebApi.retrieveMultipleRequest function and loop through all pages to rollup all records. dynamicsWebApi.countAll will be available soon.
dynamicsWebApi.count("leads", "statecode eq 0").then(function (count) {
})
.catch(function (error) {
});
Associate
var accountId = '00000000-0000-0000-0000-000000000001';
var leadId = '00000000-0000-0000-0000-000000000002';
dynamicsWebApi.associate("accounts", accountId, "lead_parent_account", "leads", leadId).then(function () {
}).catch(function (error) {
});
Associate for a single-valued navigation property
The name of a single-valued navigation property can be retrieved by using a GET
request with a header Prefer:odata.include-annotations=Microsoft.Dynamics.CRM.associatednavigationproperty
,
then individual records in the response will contain the property @Microsoft.Dynamics.CRM.associatednavigationproperty
which is the name of the needed navigation property.
Usually it will be equal to a schema name of the entity attribute.
For example, there is an entity with a logical name new_test
, it has a lookup attribute to lead
entity called new_parentlead
and schema name new_ParentLead
which is needed single-valued navigation property.
var new_testid = '00000000-0000-0000-0000-000000000001';
var leadId = '00000000-0000-0000-0000-000000000002';
dynamicsWebApi.associateSingleValued("new_tests", new_testid, "new_ParentLead", "leads", leadId)
.then(function () {
}).catch(function (error) {
});
Disassociate
var accountId = '00000000-0000-0000-0000-000000000001';
var leadId = '00000000-0000-0000-0000-000000000002';
dynamicsWebApi.disassociate("accounts", accountId, "lead_parent_account", leadId).then(function () {
}).catch(function (error) {
});
Disassociate for a single-valued navigation property
Current request removes a reference to an entity for a single-valued navigation property. The following code snippet uses an example shown in Associate for a single-valued navigation property.
var new_testid = '00000000-0000-0000-0000-000000000001';
dynamicsWebApi.disassociateSingleValued("new_tests", new_testid, "new_ParentLead").then(function () {
}).catch(function (error) {
});
Fetch XML Request
var fetchXml = '<fetch mapping="logical">' +
'<entity name="account">' +
'<attribute name="accountid"/>' +
'<attribute name="name"/>' +
'</entity>' +
'</fetch>';
dynamicsWebApi.executeFetchXml("accounts", fetchXml).then(function (response) {
})
.catch(function (error) {
});
Paging
var fetchXml = '<fetch mapping="logical">' +
'<entity name="account">' +
'<attribute name="accountid"/>' +
'<attribute name="name"/>' +
'</entity>' +
'</fetch>';
dynamicsWebApi.executeFetchXml("accounts", fetchXml).then(function (response) {
return dynamicsWebApi
.executeFetchXml("accounts", fetchXml, null, response.PagingInfo.nextPage, response.PagingInfo.cookie);
}).then(function (response) {
return dynamicsWebApi
.executeFetchXml("accounts", fetchXml, null, response.PagingInfo.nextPage, response.PagingInfo.cookie);
}).then(function (response) {
})
Execute Web API functions
Bound functions
var teamId = "00000000-0000-0000-0000-000000000001";
dynamicsWebApi.executeBoundFunction(teamId, "teams", "Microsoft.Dynamics.CRM.RetrieveTeamPrivileges")
.then(function (response) {
}).catch(function (error) {
});
Unbound functions
var parameters = {
LocalizedStandardName: 'Pacific Standard Time',
LocaleId: 1033
};
dynamicsWebApi.executeUnboundFunction("GetTimeZoneCodeByLocalizedName", parameters).then(function (result) {
var timeZoneCode = result.TimeZoneCode;
}).catch(function (error) {
});
Execute Web API actions
Bound actions
var queueId = "00000000-0000-0000-0000-000000000001";
var letterActivityId = "00000000-0000-0000-0000-000000000002";
var actionRequest = {
Target: {
activityid: letterActivityId,
"@odata.type": "Microsoft.Dynamics.CRM.letter"
}
};
dynamicsWebApi.executeBoundAction(queueId, "queues", "Microsoft.Dynamics.CRM.AddToQueue", actionRequest)
.then(function (result) {
var queueItemId = result.QueueItemId;
})
.catch(function (error) {
});
Unbound actions
var opportunityId = "b3828ac8-917a-e511-80d2-00155d2a68d2";
var actionRequest = {
Status: 3,
OpportunityClose: {
subject: "Won Opportunity",
"opportunityid@odata.bind": "opportunities(" + opportunityId + ")"
}
};
dynamicsWebApi.executeUnboundAction("WinOpportunity", actionRequest).then(function () {
}).catch(function (error) {
});
In Progress
Many more features to come!
Thank you for your patience!
JavaScript Promises
Please use the following library that implements ES6 Promises: DynamicsWebApi with Promises.
It is highly recommended to use one of the Promise Polyfills (Yaku, ES6 Promise and etc.) if DynamicsWebApi is intended to be used in the browsers.
JavaScript Callbacks
Please use the following library that implements Callbacks : DynamicsWebApi with Callbacks.