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.
Stable versions are available!
I made a decision to convert ASP.NET project to Node.js one, meanwhile fully tested and stable browser versions can be found in scripts
folder.
- [DynamicsWebApi Promises version] (/scripts/dynamics-web-api.js)
- [DynamicsWebApi Callbacks version] (/scripts/dynamics-web-api-callbacks.js)
Any suggestions are welcome!
Table of Contents
- Quick Start
- [Configuration] (#configuration)
- [Configuration Object Properties] (#configuration-object-properties)
- [Intellisense] (#intellisense)
- [Request Examples] (#request-examples)
- [Create a record] (#create-a-record)
- [Update a record] (#update-a-record)
- [Update a single property value] (#update-a-single-property-value)
- [Upsert a record] (#upsert-a-record)
- [Delete a record] (#delete-a-record)
- [Delete a single property value] (#delete-a-single-property-value)
- [Retrieve a record] (#retrieve-a-record)
- [Retrieve multiple records] (#retrieve-multiple-records)
- [Count] (#count)
- [Associate] (#associate)
- [Associate for a single-valued navigation property] (#associate-for-a-single-valued-navigation-property)
- [Disassociate] (#disassociate)
- [Disassociate for a single-valued navigation property] (#disassociate-for-a-single-valued-navigation-property)
- [Fetch XML Request] (#fetch-xml-request)
- [Execute Web API functions] (#execute-web-api-functions)
- [Execute Web API actions] (#execute-web-api-actions)
- [JavaScript Promises] (#javascript-promises)
- [JavaScript Callbacks] (#javascript-callbacks)
Quick Start
In order to use a library DynamicsWebApi.js needs to be added as a Web Resource in CRM.
Configuration
To initialize a new instance of DynamicsWebApi helper with a different configuration, please use the following code:
var dynamicsWebApi = new DynamicsWebApi({ webApiVersion: "8.1" });
To set a configuration dynamically (if needed):
dynamicsWebApi.setConfig({ webApiVersion: "8.2" });
Configuration Object Properties
Property Name | Type | Description |
---|
impersonate | String | A String representing the GUID value for the Dynamics 365 system user id. Impersonates the user. |
webApiUrl | String | A complete URL string to Web API. Example of the URL: "https:/myorg.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. By default version "8.0" used. |
At this moment the library only works inside CRM.
Intellisense
The work on a rich Intellisense experience is still on going but developers can already use some of the features.
DWA Object
DWA is a JavaScript object located inside DynamicsWebApi library file. This object enables Intellisense for Visual Studio and therefore improves an overall productivity of a software developer.
The object contains several child objects which are described below.
DWA.Types
DWA.Types
contains various objects (or so called Types) that are used in DynamicsWebApi and can be used by developers to enable Intellisense for some of the functions.
At this moment only Reponse objects are described there.
The following example shows how to use DWA.Types
in the code:
Let's assume that we needed to write a retrieveMultipleRequest.
dynamicsWebApi.retrieveMultipleRequest(request).then(function (response) {
})
This function returns an object which properties are not known at the runtime. This problem
can be easily resolved by using XML Documentation Comments for JavaScript.
dynamicsWebApi.retrieveMultipleRequest(request).then(function (response) {
var count = response.oDataCount;
var nextPageLink = response.oDataNextLink;
var records = response.value;
})
The following types are described at this moment: DWA.Types.ReferenceResponse
, DWA.Types.FetchXmlResponse
and DWA.Types.MultipleResponse
.
DWA.Prefer
DWA.Prefer
contains various values that can header "Prefer" to be set with. Most of the existing operations support such header. The following list describes which
header values can be used for which operations.
DWA.Prefer.ReturnRepresentation
- create
, update
, updateSingleProperty
- allows developers to retrieve just created or updated object in a single request.DWA.Prefer.Annotations
- retrieveRequest
, retrieveMultipleRequest
, executeFetchXml
- allows to retrieve additional information about lookups, option sets and etc.
Examples:
dynamicsWebApi.update(recordId, "leads", lead, DWA.Prefer.ReturnRepresentation);
var request = {
id: recordId,
collection: "leads",
includeAnnotations: DWA.Prefer.Annotations.FormattedValue
};
dynamicsWebApi.retrieveRequest(request);
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] (https://msdn.microsoft.com/en-us/library/mt607711.aspx). |
ifnonematch | String | retrieveRequest , upsertRequest | Sets If-None-Match header value that enables to use conditional retrieval in applicable requests. [More info] (https://msdn.microsoft.com/en-us/library/mt607711.aspx). |
impersonate | String | All | A String representing the GUID value for the Dynamics 365 system user id. Impersonates the user. |
includeAnnotations | String | retrieveRequest , retrieveMultipleRequest | 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. |
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] (https://community.dynamics.com/crm/b/joegilldynamicscrm/archive/2016/03/23/web-api-querying-with-expand)
$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) {
})
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: DWA.Prefer.Annotations.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] (https://msdn.microsoft.com/en-us/library/mt607990.aspx#Anchor_5)
and it must have a suffix /$ref
attached to it. The returned object will be DWA.Types.ReferenceResponse
. 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] (https://msdn.microsoft.com/en-us/library/mt607990.aspx#Anchor_5)
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' count='5'>" +
"<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.
Recommended
- Yaku - ES6-Promise polyfill.
JavaScript Callbacks
Please use the following library that implements Callbacks : DynamicsWebApi with Callbacks.