DynamicsWebApi for Microsoft Dynamics 365 CE (CRM) / Microsoft Dataverse Web API (formerly known as Microsoft Common Data Service Web API)
![npm](https://img.shields.io/npm/dt/dynamics-web-api?style=flat-square)
DynamicsWebApi is a Microsoft Dynamics 365 CE (CRM) / Microsoft Dataverse (formerly: Common Data Service) Web API helper library written in JavaScript.
It is compatible with: Microsoft Dataverse (formerly: Microsoft Common Data Service), Microsoft Dynamics 365 CE (online), Microsoft Dynamics 365 CE (on-premises), Microsoft Dynamics CRM 2016, Microsoft Dynamics CRM Online.
Please check DynamicsWebApi Wiki where you will find documentation to DynamicsWebApi API and more.
Libraries for browsers can be found in dist folder.
I maintain this project in my free time and, to be honest with you, it takes a considerable amount of time to make sure that the library has all new features, gets improved and all raised tickets have been answered and fixed in a short amount of time. If you feel that this project has saved your time and you would like to support it, then please feel free to sponsor it through GitHub Sponsors or send a donation directly to my PayPal:
. GitHub button can be found on the project's page.
Also, please check suggestions and contributions section to learn more on how you can help to improve this project.
DynamicsWebApi v2 is coming!
v2 will be written in TypeScript and include numerous optimizations.
There will be breaking changes between v1 and v2:
- v2 will not have simple requests, only the advanced ones (with request objects), therefore I highly recommend using request objects for making requests.
- DynamicsWebApi Callbacks will not be a part of v2.
I am very excited to finally release the new version and I hope you too! Stay tuned!
Please note, that "Dynamics 365" in this readme refers to Microsoft Dynamics 365 Customer Engagement / Microsoft Dataverse (formerly known as Microsoft Common Data Service).
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 form or refer to it in your HTML Web Resource and then initialize the main object:
const dynamicsWebApi = new DynamicsWebApi();
dynamicsWebApi.executeUnboundFunction("WhoAmI").then(function (response) {
Xrm.Navigation.openAlertDialog({
text: "Hello Dynamics 365! My id is: " + response.UserId,
title: "DynamicsWebApi Test"
});
}).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:
const DynamicsWebApi = require('dynamics-web-api');
import DynamicsWebApi from '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 MSAL for JS or you can write your own functionality, as it is described here.
Here is a sample using @azure/msal-node
:
import Config from './config.js';
import DynamicsWebApi from 'dynamics-web-api';
import * as MSAL from '@azure/msal-node';
const authorityUrl = 'https://login.microsoftonline.com/<COPY A GUID HERE>';
const msalConfig = {
auth: {
authority: authorityUrl,
clientId: Config.clientId,
clientSecret: Config.secret,
knownAuthorities: ['login.microsoftonline.com']
}
}
const cca = new MSAL.ConfidentialClientApplication(msalConfig);
const serverUrl = 'https://<YOUR ORG HERE>.api.crm.dynamics.com';
const acquireToken = (dynamicsWebApiCallback) => {
cca.acquireTokenByClientCredential({
scopes: [`${serverUrl}/.default`],
}).then(response => {
dynamicsWebApiCallback(response.accessToken);
}).catch((error) => {
console.log(JSON.stringify(error));
});
}
const dynamicsWebApi = new DynamicsWebApi({
webApiUrl: `${serverUrl}/api/data/v9.2/`,
onTokenRefresh: acquireToken
});
try{
const reponse = await dynamicsWebApi.executeUnboundFunction('WhoAmI');
console.log(`Hello Dynamics 365! My id is: ${response.UserId}`);
}
catch (error){
console.log(error);
}
Configuration
To initialize a new instance of DynamicsWebApi with a configuration object, please use the following code:
Web browser
var dynamicsWebApi = new DynamicsWebApi({ webApiVersion: '9.1' });
Node.js
var dynamicsWebApi = new DynamicsWebApi({
webApiUrl: 'https://myorg.api.crm.dynamics.com/api/data/v9.1/',
onTokenRefresh: acquireToken
});
You can set a configuration dynamically if needed:
dynamicsWebApi.setConfig({ webApiVersion: '9.1' });
Configuration Parameters
Property Name | Type | Description |
---|
impersonate | String | A String representing the GUID value for the Dynamics 365 system user id. Impersonates the user. |
impersonateAAD | String | v.1.6.12+ A String representing the GUID value for the Azure active directory object id. Impersonates the user. More Info |
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). |
proxy | Object | v.1.7.2+ Proxy configuration object. More Info |
returnRepresentation | Boolean | Defaults Prefer header with value "return=representation". Use this property to return just created or updated entity in a single request. |
timeout | Number | Sets a number of milliseconds before a request times out. |
useEntityNames | Boolean | v.1.4.0+ Indicates whether to use entity logical names instead of collection logical names during requests. |
webApiUrl | String | A complete URL string to Web API. Example of the URL: "https://myorg.api.crm.dynamics.com/api/data/v9.1/". 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 CRM.
Property webApiUrl
is required when DynamicsWebApi used externally.
Important! If both configuration properties set then webApiUrl
will have a higher priority than webApiVersion
, so the last one will be skipped.
Important! Please note, if you are using DynamicsWebApi
outside Microsoft Dynamics 365 and set useEntityNames
to true
the first request to Web Api
will fetch LogicalCollectionName
and LogicalName
from entity metadata for all entities. It does not happen when DynamicsWebApi
is used in Microsoft Dynamics 365 Web Resources (there is no additional request, no impact on perfomance).
Request Examples
DynamicsWebApi supports Basic and Advanced calls to Web API.
Basic calls can be made by using functions with the most common input parameters. They are 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
, retrieveAll
, count
, countAll
,
executeFetchXml
, executeFetchXmlAll
, associate
, disassociate
, associateSingleValued
, disassociateSingleValued
, executeBoundFunction
,
executeUnboundFunction
, executeBoundAction
, executeUnboundAction
.
Advanced functions have a suffix Request
added to the end of the applicable operation (exceptions are uploadFile
and downloadFile
).
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 |
---|
apply | String | retrieveMultipleRequest , retrieveAllRequest | v1.6.4+ Sets the $apply system query option to aggregate and group your data dynamically. More Info |
async | Boolean | All | XHR requests only! Indicates whether the requests should be made synchronously or asynchronously. Default value is true (asynchronously). |
bypassCustomPluginExecution | Boolean | createRequest , updateRequest , upsertRequest , deleteRequest | v1.7.5+ If set to true, the request bypasses custom business logic, all synchronous plug-ins and real-time workflows are disabled. Check for special exceptions in Microsft Docs. More Info |
collection | String | All | The name of the Entity Collection (or Entity Logical name in v1.4.0+ ). |
contentId | String | createRequest , updateRequest , upsertRequest , deleteRequest | v1.5.6+ BATCH REQUESTS ONLY! Sets Content-ID header or references request in a Change Set. More Info |
count | Boolean | retrieveMultipleRequest , retrieveAllRequest | 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! |
data | ArrayBuffer / Buffer (for node.js) | uploadFile | v.1.7.0+ Web API v9.1+ only! File buffer for uploading to File Attributes. |
duplicateDetection | Boolean | createRequest , updateRequest , upsertRequest | v.1.3.4+ Web API v9+ only! Boolean that enables duplicate detection. More Info |
entity | Object | createRequest , 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 , retrieveMultipleRequest , createRequest , 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. |
fieldName | String | uploadFile , downloadFile , deleteRequest | v.1.7.0+ Web API v9.1+ only! Use this option to specify the name of the file attribute in Dynamics 365. More Info |
fileName | String | uploadFile | v.1.7.0+ Web API v9.1+ only! Specifies the name of the file |
filter | String | retrieveRequest , retrieveMultipleRequest , retrieveAllRequest | Use the $filter system query option to set criteria for which entities will be returned. |
id | String | retrieveRequest , createRequest , updateRequest , upsertRequest , deleteRequest | deprecated in v.1.3.4 Use key field, instead of id . A String representing the Primary Key (GUID) of 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. |
impersonateAAD | String | All | v.1.6.12+ A String representing the GUID value for the Azure active directory object id. Impersonates the user. More Info |
includeAnnotations | String | retrieveRequest , retrieveMultipleRequest , retrieveAllRequest , createRequest , 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. |
key | String | retrieveRequest , createRequest , updateRequest , upsertRequest , deleteRequest , uploadFile , downloadFile | v.1.3.4+ A String representing collection record's Primary Key (GUID) or Alternate Key(s). |
maxPageSize | Number | retrieveMultipleRequest , retrieveAllRequest | Sets the odata.maxpagesize preference value to request the number of entities returned in the response. |
mergeLabels | Boolean | updateRequest | v.1.4.2+ Metadata Update only! Sets MSCRM.MergeLabels header that controls whether to overwrite the existing labels or merge your new label with any existing language labels. Default value is false . More Info |
metadataAttributeType | String | retrieveRequest , updateRequest | v.1.4.3+ Casts the Attributes to a specific type. (Used in requests to Attribute Metadata) More Info |
navigationProperty | String | retrieveRequest , createRequest , updateRequest | 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. |
navigationPropertyKey | String | retrieveRequest , createRequest , updateRequest | v.1.4.3+ A String representing navigation property's Primary Key (GUID) or Alternate Key(s). (For example, to retrieve Attribute Metadata) |
noCache | Boolean | All | v.1.4.0+ If set to true , DynamicsWebApi adds a request header Cache-Control: no-cache . Default value is false . |
orderBy | Array | retrieveMultipleRequest , retrieveAllRequest | 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 | createRequest , 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 , retrieveAllRequest , updateRequest , upsertRequest | An Array (of Strings) representing the $select OData System Query Option to control which attributes will be returned. |
timeout | Number | All | v.1.6.10+ Sets a number of milliseconds before a request times out. |
token | String | All | Authorization Token. If set, onTokenRefresh will not be called. |
top | Number | retrieveMultipleRequest , retrieveAllRequest | Limit the number of results returned by using the $top system query option. Do not use $top with $count! |
trackChanges | Boolean | retrieveMultipleRequest , retrieveAllRequest | v.1.5.11+ Sets Prefer header with value 'odata.track-changes' to request that a delta link be returned which can subsequently be used to retrieve entity changes. Important! Change Tracking must be enabled for the entity. More Info |
userQuery | String | retrieveRequest | A String representing the GUID value of the user query. |
Basic and Advanced functions 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 |
---|
expand | Array | An array of Expand Objects representing the $expand OData System Query Option value to control which related records are also returned. |
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. |
Starting from version 1.2.8, all requests to Web API that have long URLs (more than 2000 characters) are automatically converted to a Batch Request.
This feature is very convenient when you make a call with big Fetch XMLs. No special parameters needed to do a convertation.
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"])
Advanced using Request Object
var lead = {
subject: "Test WebAPI",
firstname: "Test",
lastname: "WebAPI",
jobtitle: "Title"
};
var request = {
collection: "leads",
entity: lead,
returnRepresentation: true
}
dynamicsWebApi.createRequest(request).then(function (record) {
var subject = record.subject;
}).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 = {
key: '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 = {
key: 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 = {
key: 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 = {
key: '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 single-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 = {
key: recordId,
collection: "new_tests",
navigationProperty: "new_ParentLead",
select: ["fullname", "subject"]
}
request = {
key: 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){
});
Change Tracking
var request = {
collection: "leads",
select: ["fullname", "subject"],
trackChanges: true
};
dynamicsWebApi.retrieveMultipleRequest(request).then(function (response) {
var deltaLink = response.oDataDeltaLink;
return dynamicsWebApi.retrieveMultipleRequest(request, response.oDataDeltaLink);
})
.then(function (response) {
})
.catch(function (error){
});
Retrieve All records
The following function retrieves records and goes through all pages automatically.
dynamicsWebApi.retrieveAll("leads", ["fullname", "subject"], "statecode eq 0").then(function (response) {
var records = response.value;
})
.catch(function (error){
});
OR advanced function:
var request = {
collection: "leads",
select: ["fullname", "subject"],
filter: "statecode eq 0",
maxPageSize: 5
};
dynamicsWebApi.retrieveAllRequest(request).then(function (response) {
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.
dynamicsWebApi.count("leads", "statecode eq 0").then(function (count) {
})
.catch(function (error) {
});
Count limitation workaround
The following function can be used to count all records in a collection. It's a workaround and just counts the number of objects in the array
returned in retrieveAllRequest
.
dynamicsWebApi.countAll("leads", "statecode eq 0").then(function (count) {
})
.catch(function (error) {
});
Downside of this workaround is that it does not only return a count number but also all data for records in a collection. In order to make a small
optimisation I added the third parameter to the function that can be used to reduce the length of the response. The third parameter represents
a select query option.
dynamicsWebApi.countAll("leads", "statecode eq 0", ["subject"]).then(function (count) {
})
.catch(function (error) {
});
FYI, in the majority of cases it is better to use Fetch XML aggregation, but take into a consideration that it is also limited to 50000 records
by default.
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) {
});
Starting from version 1.2.5 DynamicsWebApi has an alias with a shorter name and same parameters: dynamicsWebApi.fetch(...)
,
that works in the same way as executeFetchXml
.
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) {
})
Fetch All records
The following function executes a FetchXml and goes through all pages automatically:
var fetchXml = '<fetch mapping="logical">' +
'<entity name="account">' +
'<attribute name="accountid"/>' +
'<attribute name="name"/>' +
'</entity>' +
'</fetch>';
dynamicsWebApi.executeFetchXmlAll("accounts", fetchXml).then(function (response) {
})
Starting from version 1.2.5 DynamicsWebApi has an alias with a shorter name and same parameters: dynamicsWebApi.fetchAll(...)
,
that works in the same way as executeFetchXmlAll
.
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) {
});
Execute Batch Operations
version 1.5.0+
Batch requests bundle multiple operations into a single one and have the following advantages:
- Reduces a number of requests sent to the Web API server.
Each user is allowed up to 60,000 API requests, per organization instance, within five minute sliding interval.
More Info - Provides a way to run multiple operations in a single transaction. If any operation that changes data (within a single changeset) fails all completed ones will be rolled back.
- All operations within a batch request run consequently (FIFO).
DynamicsWebApi provides a straightforward way to execute Batch operations which may not always be simple to compose.
The following example bundles 2 retrieve multiple operations and an update:
dynamicsWebApi.startBatch();
dynamicsWebApi.retrieveMultiple('accounts');
dynamicsWebApi.update('00000000-0000-0000-0000-000000000002', 'contacts', { firstname: "Test", lastname: "Batch!" });
dynamicsWebApi.retrieveMultiple('contacts');
dynamicsWebApi.executeBatch()
.then(function (responses) {
var accounts = responses[0];
var isUpdated = responses[1];
var contacts = responses[2];
}).catch(function (error) {
});
The next example shows how to run multiple operations in a single transaction which means if at least one operation fails all completed ones will be rolled back which ensures a data consistency.
var order1 = {
name: '1 year membership',
'customerid_contact@odata.bind': 'contacts(00000000-0000-0000-0000-000000000001)'
};
var order2 = {
name: 'book',
'customerid_contact@odata.bind': 'contacts(00000000-0000-0000-0000-000000000001)'
};
dynamicsWebApi.startBatch();
dynamicsWebApi.create(order1, 'salesorders');
dynamicsWebApi.create(order2, 'salesorders');
dynamicsWebApi.executeBatch().then(function (responses) {
var salesorderId1 = responses[0];
var salesorderId2 = responses[1];
}).catch(function (error) {
alert('Cannot complete a checkout. Please try again later.');
});
Important! Developers who use DynamicsWebApi with callbacks do not need to pass successCallback
and errorCallback
in an individual operation when startBatch()
is called,
just pass null
if you need to add additional parameters in the request,
for example: dynamicsWebApi.deleteRecord('00000000-0000-0000-0000-000000000001', 'contacts', null, null, 'firstname')
.
Use Content-ID to reference requests in a Change Set
version 1.5.6+
You can reference a request in a Change Set. For example, if you want to create related entities in a single batch request:
var order = {
name: '1 year membership'
};
var contact = {
firstname: 'John',
lastname: 'Doe'
};
dynamicsWebApi.startBatch();
dynamicsWebApi.createRequest({ entity: order, collection: 'salesorders', contentId: '1' });
dynamicsWebApi.createRequest({ entity: contact, collection: 'customerid_contact', contentId: '$1' });
dynamicsWebApi.executeBatch()
.then(function (responses) {
var salesorderId = responses[0];
}).catch(function (error) {
});
Note that if you are making a request to a navigation property (collection: 'customerid_contact'
), the request won't have a response, it is an OOTB Web API limitation.
Important! DynamicsWebApi automatically assigns value to a Content-ID
if it is not provided, therefore, please set your Content-ID
value less than 100000.
Use Content-ID inside a request payload
version 1.5.7+
Another option to make the same request is to use Content-ID
reference inside a request payload as following:
var contact = {
firstname: 'John',
lastname: 'Doe'
};
var order = {
name: '1 year membership',
'customerid_contact@odata.bind': '$1'
};
dynamicsWebApi.startBatch();
dynamicsWebApi.createRequest({ entity: contact, collection: 'contacts', contentId: '1' });
dynamicsWebApi.createRequest({ entity: order, collection: 'salesorders' });
dynamicsWebApi.executeBatch()
.then(function (responses) {
var contactId = responses[0];
var salesorderId = responses[1];
}).catch(function (error) {
});
Important! Web API seems to have a limitation (or a bug) where it does not return the response with returnRepresentation
set to true
. It happens only if you are trying to return a representation of an entity that is being
linked to another one in a single request. More Info and examples is in this issue..
Limitations
Currently, there are some limitations in DynamicsWebApi Batch Operations:
- Operations that use pagination to recursively retrieve all records cannot be used in a 'batch mode'. These include:
retrieveAll
, retrieveAllRequest
, countAll
, fetchAll
, executeFetchXmlAll
.
You will get an error saying that the operation is incompatible with a 'batch mode'. - Does not apply to
v.1.6.5+
: The following limitation is for external applications (working outside D365 CE forms). useEntityNames
may not work in a 'batch mode' if it is set to true
.
To make sure that it works, please execute any operation before calling dynamicsWebApi.startBatch()
so that it caches all entity names, for example: dynamicsWebApi.count('account')
.
There are also out of the box Web API limitations for batch operations:
- Batch requests can contain up to 1000 individual requests and cannot contain other batch requests.
- The
odata.continue-on-error
preference is not supported by the Web API. Any error that occurs in the batch will stop the processing of the remainder of the batch.
You can find an official documentation that covers Web API batch requests here: Execute batch operations using the Web API.
Work with Metadata Definitions
Version 1.4.3+
Before working with metadata read the following section from Microsoft Documentation.
Create Entity
var entityDefinition = {
"@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata",
"Attributes": [
{
"AttributeType": "String",
"AttributeTypeName": {
"Value": "StringType"
},
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Type the name of the bank account",
"LanguageCode": 1033
}]
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Account Name",
"LanguageCode": 1033
}]
},
"IsPrimaryName": true,
"RequiredLevel": {
"Value": "None",
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyrequirementlevelsettings"
},
"SchemaName": "new_AccountName",
"@odata.type": "Microsoft.Dynamics.CRM.StringAttributeMetadata",
"FormatName": {
"Value": "Text"
},
"MaxLength": 100
}],
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "An entity to store information about customer bank accounts",
"LanguageCode": 1033
}]
},
"DisplayCollectionName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Bank Accounts",
"LanguageCode": 1033
}]
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Bank Account",
"LanguageCode": 1033
}]
},
"HasActivities": false,
"HasNotes": false,
"IsActivity": false,
"OwnershipType": "UserOwned",
"SchemaName": "new_BankAccount"
};
dynamicsWebApi.createEntity(entityDefinition).then(function(entityId){
}).catch(function(error){
})
Retrieve Entity
Entity Metadata can be retrieved by either Primary Key (MetadataId) or by an Alternate Key (LogicalName). More Info
var entityKey = '00000000-0000-0000-0000-000000000001';
dynamicsWebApi.retrieveEntity(entityKey, ['SchemaName', 'LogicalName']).then(function(entityMetadata){
var schemaName = entityMetadata.SchemaName;
}).catch(function(error){
});
Update Entity
Microsoft recommends to make changes in the entity metadata that has been priorly retrieved to avoid any mistake. I would also recommend to read information about MSCRM.MergeLabels header prior updating metadata. More information about the header can be found here.
Important! Make sure you set MetadataId
property when you update the metadata, DynamicsWebApi use it as a primary key for the EntityDefinition record.
var entityKey = "LogicalName='new_accountname'";
dynamicsWebApi.retrieveEntity(entityKey).then(function(entityMetadata){
entityMetadata.DispalyName.LocalizedLabels[0].Label = 'New Bank Account';
return dynamicsWebApi.updateEntity(entityMetadata);
}).catch(function(error){
});
Important! When you update an entity, you must publish changes in CRM. More Info
Retrieve Multiple Entities
dynamicsWebApi.retrieveEntities(['LogicalName'], "OwnershipType eq Microsoft.Dynamics.CRM.OwnershipTypes'UserOwned'").then(function(response){
var firstLogicalName = response.value[0].LogicalName;
}).catch(function(error){
});
Create Attribute
var entityKey = '00000000-0000-0000-0000-000000000001';
var attributeDefinition = {
"AttributeType": "Money",
"AttributeTypeName": {
"Value": "MoneyType"
},
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Enter the balance amount",
"LanguageCode": 1033
}]
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Balance",
"LanguageCode": 1033
}]
},
"RequiredLevel": {
"Value": "None",
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyrequirementlevelsettings"
},
"SchemaName": "new_Balance",
"@odata.type": "Microsoft.Dynamics.CRM.MoneyAttributeMetadata",
"PrecisionSource": 2
};
dynamicsWebApi.createAttribute(entityKey, attributeDefinition).then(function(attributeId){
}).catch(function(error){
});
Retrieve Attribute
Attribute Metadata can be retrieved by either Primary Key (MetadataId) or by an Alternate Key (LogicalName). More Info
The following example will retrieve only common properties available in AttributeMetadata entity.
var entityKey = '00000000-0000-0000-0000-000000000001';
var attributeKey = '00000000-0000-0000-0000-000000000002';
dynamicsWebApi.retrieveAttribute(entityKey, attributeKey, ['SchemaName']).then(function(attributeMetadata){
var schemaName = attributeMetadata.SchemaName;
}).catch(function(error){
});
Use parameter in the function to cast the attribute to a specific type.
var entityKey = '00000000-0000-0000-0000-000000000001';
var attributeKey = '00000000-0000-0000-0000-000000000002';
dynamicsWebApi.retrieveAttribute(entityKey, attributeKey, ['SchemaName'], 'Microsoft.Dynamics.CRM.MoneyAttributeMetadata')
.then(function(attributeMetadata){
var schemaName = attributeMetadata.SchemaName;
}).catch(function(error){
});
Update Attribute
Important! Make sure you set MetadataId
property when you update the metadata, DynamicsWebApi use it as a primary key for the EntityDefinition record.
The following example will update only common properties availible in AttributeMetadata entity. If you need to update specific properties of Attributes with type that inherit from the AttributeMetadata you will need to cast the attribute to the specific type. More Info
var entityKey = "LogicalName='new_accountname'";
var attributeKey = "LogicalName='new_balance'";
dynamicsWebApi.retrieveAttribute(entityKey, attributeKey).then(function(attributeMetadata){
attributeMetadata.DispalyName.LocalizedLabels[0].Label = 'New Balance';
return dynamicsWebApi.updateAttribute(entityKey, attributeMetadata);
}).catch(function(error){
});
To cast a property to a specific type use a parameter in the function.
var entityKey = "LogicalName='new_accountname'";
var attributeKey = "LogicalName='new_balance'";
var attributeType = 'Microsoft.Dynamics.CRM.MoneyAttributeMetadata';
dynamicsWebApi.retrieveAttribute(entityKey, attributeKey, attributeType).then(function(attributeMetadata){
attributeMetadata.DispalyName.LocalizedLabels[0].Label = 'New Balance';
return dynamicsWebApi.updateAttribute(entityKey, attributeMetadata, attributeType);
}).catch(function(error){
});
Important! Make sure you include the attribute type in the update function as well.
Important! When you update an attribute, you must publish changes in CRM. More Info
Retrieve Multiple Attributes
The following example will retrieve only common properties available in AttributeMetadata entity.
var entityKey = "LogicalName='new_accountname'";
dynamicsWebApi.retrieveAttributes(entityKey).then(function(response){
var firstAttribute = response.value[0];
}).catch(function(error){
});
To retrieve only attributes of a specific type use a parameter in a function:
var entityKey = "LogicalName='new_accountname'";
dynamicsWebApi.retrieveAttributes(entityKey, 'Microsoft.Dynamics.CRM.MoneyAttributeMetadata').then(function(response){
var firstAttribute = response.value[0];
}).catch(function(error){
});
Use requests to query Entity and Attribute metadata
You can also use common request functions to create, retrieve and update entity and attribute metadata. Just use the following rules:
- Always set
collection: 'EntityDefinitions'
. - To retrieve a specific entity metadata by a Primary or Alternate Key use
key
property. For example: key: 'LogicalName="account"'
. - To get attributes, set
navigationProperty: 'Attributes'
. - To retrieve a specific attribute metadata by Primary or Alternate Key use
navigationPropertyKey
. For example: navigationPropertyKey: '00000000-0000-0000-0000-000000000002'
. - During entity or attribute metadata update you can use
mergeLabels
property to set MSCRM.MergeLabels attribute. By default mergeLabels: false
. - To send entity or attribute definition use
entity
property.
Examples
Retrieve entity metadata with attributes (with common properties):
var request = {
collection: 'EntityDefinitions',
key: '00000000-0000-0000-0000-000000000001',
select: ['LogicalName', 'SchemaName'],
expand: 'Attributes'
};
dynamicsWebApi.retrieveRequest(request).then(function(entityMetadata){
var attributes = entityMetadata.Attributes;
}).catch(function(error){
});
Retrieve attribute metadata and cast it to the StringType:
var request = {
collection: 'EntityDefinitions',
key: 'LogicalName="account"',
navigationProperty: 'Attributes',
navigationPropertyKey: 'LogicalName="firstname"',
metadataAttributeType: 'Microsoft.Dynamics.CRM.StringAttributeMetadata'
};
dynamicsWebApi.retrieveRequest(request).then(function(attributeMetadata){
var displayNameDefaultLabel = attributeMetadata.DisplayName.LocalizedLabels[0].Label;
}).catch(function(error){
});
Update entity metadata with MSCRM.MergeLabels header set to true
:
var request = {
collection: 'EntityDefinitions',
key: 'LogicalName="account"'
};
dynamicsWebApi.retrieveRequest(request).then(function(entityMetadata){
entityMetadata.DisplayName.LocalizedLabels[0].Label = 'Organization';
var updateRequest = {
collection: 'EntityDefinitions',
key: entityMetadata.MetadataId,
mergeLabels: true,
entity: entityMetadata
};
return dynamicsWebApi.updateRequest(updateRequest);
}).catch(function(error){
});
dynamicsWebApi.retrieveEntity('LogicalName="account"').then(function(entityMetadata){
entityMetadata.DisplayName.LocalizedLabels[0].Label = 'Organization';
return dynamicsWebApi.updateEntity(entityMetadata, true);
}).catch(function(error){
});
Create Relationship
var newRelationship = {
"SchemaName": "dwa_contact_dwa_dynamicswebapitest",
"@odata.type": "Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",
"AssociatedMenuConfiguration": {
"Behavior": "UseCollectionName",
"Group": "Details",
"Label": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "DWA Test",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "DWA Test",
"LanguageCode": 1033
}
},
"Order": 10000
},
"CascadeConfiguration": {
"Assign": "Cascade",
"Delete": "Cascade",
"Merge": "Cascade",
"Reparent": "Cascade",
"Share": "Cascade",
"Unshare": "Cascade"
},
"ReferencedAttribute": "contactid",
"ReferencedEntity": "contact",
"ReferencingEntity": "dwa_dynamicswebapitest",
"Lookup": {
"AttributeType": "Lookup",
"AttributeTypeName": {
"Value": "LookupType"
},
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "The owner of the test",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "The owner of the test",
"LanguageCode": 1033
}
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "DWA Test Owner",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "DWA Test Owner",
"LanguageCode": 1033
}
},
"RequiredLevel": {
"Value": "ApplicationRequired",
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyrequirementlevelsettings"
},
"SchemaName": "dwa_TestOwner",
"@odata.type": "Microsoft.Dynamics.CRM.LookupAttributeMetadata"
}
};
dynamicsWebApi.createRelationship(newRelationship).then(function (relationshipId) {
}).catch(function (error) {
});
Update Relationship
Important! Make sure you set MetadataId
property when you update the metadata, DynamicsWebApi use it as a primary key for the EntityDefinition record.
var metadataId = '10cb680e-b6a7-e811-816a-480fcfe97e21';
dynamicsWebApi.retrieveRelationship(metadataId).then(function (relationship) {
relationship.AssociatedMenuConfiguration.Label.LocalizedLabels[0].Label = "New Label";
return dynamicsWebApi.updateRelationship(relationship);
}).then(function (updateResponse) {
}).catch(function (error) {
});
Delete Relationship
var metadataId = '10cb680e-b6a7-e811-816a-480fcfe97e21';
dynamicsWebApi.deleteRelationship(metadataId).then(function (isDeleted) {
}).catch(function (error) {
});
Retrieve Relationship
var metadataId = '10cb680e-b6a7-e811-816a-480fcfe97e21';
dynamicsWebApi.retrieveRelationship(metadataId).then(function (relationship) {
}).catch(function (error) {
});
You can also cast a relationship into a specific type:
var metadataId = '10cb680e-b6a7-e811-816a-480fcfe97e21';
var relationshipType = 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata';
dynamicsWebApi.retrieveRelationship(metadataId, relationshipType).then(function (relationship) {
}).catch(function (error) {
});
Retrieve Multiple Relationships
var relationshipType = 'Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata';
dynamicsWebApi.retrieveRelationships(relationshipType, ['SchemaName', 'MetadataId'], "ReferencedEntity eq 'account'")
.then(function (relationship) {
}).catch(function (error) {
});
Create Global Option Set
version 1.4.6+
var optionSetDefinition = {
"@odata.type": "Microsoft.Dynamics.CRM.OptionSetMetadata",
IsCustomOptionSet: true,
IsGlobal: true,
IsManaged: false,
Name: "new_customglobaloptionset",
OptionSetType: "Picklist",
Options: [{
Value: 0,
Label: {
LocalizedLabels: [{
Label: "Label 1", LanguageCode: 1033
}],
UserLocalizedLabel: {
Label: "Label 1", LanguageCode: 1033
}
},
Description: {
LocalizedLabels: [],
UserLocalizedLabel: null
}
}, {
Value: 1,
Label: {
LocalizedLabels: [{
Label: "Label 2", LanguageCode: 1033
}],
UserLocalizedLabel: {
Label: "Label 2", LanguageCode: 1033
}
},
Description: {
LocalizedLabels: [],
UserLocalizedLabel: null
}
}],
Description: {
LocalizedLabels: [{
Label: "Description to the Global Option Set.", LanguageCode: 1033
}],
UserLocalizedLabel: {
Label: "Description to the Global Option Set.", LanguageCode: 1033
}
},
DisplayName: {
LocalizedLabels: [{
Label: "Display name to the Custom Global Option Set.", LanguageCode: 1033
}],
UserLocalizedLabel: {
Label: "Display name to the Custom Global Option Set.", LanguageCode: 1033
}
},
IsCustomizable: {
Value: true, "CanBeChanged": true, ManagedPropertyLogicalName: "iscustomizable"
}
};
dynamicsWebApi.createGlobalOptionSet(optionSetDefinition).then(function (id) {
}).catch(function (error) {
});
Update Global Option Set
version 1.4.6+
Important! Publish your changes after update, otherwise a label won't be modified.
var key = '6e133d25-abd1-e811-816e-480fcfeab9c1';
key = "Name='new_customglobaloptionset'";
dynamicsWebApi.retrieveGlobalOptionSet(key).then(function (response) {
response.DisplayName.LocalizedLabels[0].Label = "Updated Display name to the Custom Global Option Set.";
return dynamicsWebApi.updateGlobalOptionSet(response);
}).then(function (response) {
}).catch (function (error) {
});
Delete Global Option Set
version 1.4.6+
var key = '6e133d25-abd1-e811-816e-480fcfeab9c1';
key = "Name='new_customglobaloptionset'";
dynamicsWebApi.deleteGlobalOptionSet(key).then(function (response) {
}).catch(function (error) {
});
Retrieve Global Option Set
version 1.4.6+
var key = '6e133d25-abd1-e811-816e-480fcfeab9c1';
key = "Name='new_customglobaloptionset'";
dynamicsWebApi.retrieveGlobalOptionSet(key).then(function (response) {
}).catch (function (error) {
});
dynamicsWebApi.retrieveGlobalOptionSet(key, null, ['Name']).then(function (response) {
}).catch (function (error) {
});
dynamicsWebApi.retrieveGlobalOptionSet(key, 'Microsoft.Dynamics.CRM.OptionSetMetadata', ['Name', 'Options']).then(function (response) {
}).catch (function (error) {
});
Retrieve Multiple Global Option Sets
version 1.4.6+
dynamicsWebApi.retrieveGlobalOptionSets().then(function (response) {
var optionSet = response.value[0];
}).catch (function (error) {
});
dynamicsWebApi.retrieveGlobalOptionSets('Microsoft.Dynamics.CRM.OptionSetMetadata', ['Name', 'Options']).then(function (response) {
var optionSet = response.value[0];
}).catch (function (error) {
});
Work with File Fields
version 1.7.0+
Please make sure that you are connected to Dynamics 365 Web API with version 9.1+ to successfully use the functions. More information can be found here
Upload file
Browser
var fileElement = document.getElementById("upload");
var fileName = fileElement.files[0].name;
var fr = new FileReader();
fr.onload = function(){
var fileData = new Uint8Array(this.result);
dynamicsWebApi.uploadFile({
collection: 'dwa_filestorages',
key: '00000000-0000-0000-0000-000000000001',
fieldName: 'dwa_file',
fileName: fileName,
data: fileData
}).then(function(){
}).catch (function (error) {
});
}
fr.readAsArrayBuffer(fileElement.files[0]);
Node.JS
var fs = require('fs');
var filename = 'logo.png';
fs.readFile(filename, (err, data) => {
dynamicsWebApi.uploadFile({
collection: 'dwa_filestorages',
key: '00000000-0000-0000-0000-000000000001',
fieldName: 'dwa_file',
fileName: filename
data: data,
}).then(function() {
}).catch(function (error) {
});
});
Download file
dynamicsWebApi.downloadFile({
collection: 'dwa_filestorages',
key: '00000000-0000-0000-0000-000000000001',
fieldName: 'dwa_file'
}).then(function(result){
var fileBinary = result.data;
var fileName = result.fileName;
var fileSize = result.fileSize;
})
.catch(function (error) {
});
Delete file
dynamicsWebApi.deleteRequest({
collection: 'dwa_filestorages',
key: '00000000-0000-0000-0000-000000000001',
fieldName: 'dwa_file'
}).then(function(result){
})
.catch(function (error) {
});
Formatted Values and Lookup Properties
Starting from version 1.3.0 it became easier to access formatted values for properties and lookup data in response objects.
DynamicsWebApi automatically creates aliases for each property that contains a formatted value or lookup data.
For example:
var doNotPostEmailFormatted = response['donotpostalmail@OData.Community.Display.V1.FormattedValue'];
doNotPostEmailFormatted = response.donotpostalmail_Formatted;
var customerName = response['_customerid_value@OData.Community.Display.V1.FormattedValue'];
var customerEntityLogicalName = response['_customerid_value@Microsoft.Dynamics.CRM.lookuplogicalname'];
var customerNavigationProperty = response['_customerid_value@Microsoft.Dynamics.CRM.associatednavigationproperty'];
customerName = response._customerid_value_Formatted;
customerEntityLogicalName = response._customerid_value_LogicalName;
customerNavigationProperty = response._customerid_value_NavigationProperty;
If you still want to use old properties you can do so, they are not removed from the response, so it does not break your existing functionality.
As you have already noticed formatted and lookup data values are accesed by adding a particular suffix to a property name,
the following table summarizes it.
OData Annotation | Property Suffix |
---|
@OData.Community.Display.V1.FormattedValue | _Formatted |
@Microsoft.Dynamics.CRM.lookuplogicalname | _LogicalName |
@Microsoft.Dynamics.CRM.associatednavigationproperty | _NavigationProperty |
Using Alternate Keys
Starting from version 1.3.4, you can use alternate keys to Update, Upsert, Retrieve and Delete records. More Info
Basic usage
var alternateKey = "key='keyValue'";
dynamicsWebApi.retrieve(alternateKey, "leads", ["fullname", "subject"]).then(function (record) {
})
.catch(function (error) {
});
Advanced using Request Object
Please use key
instead of id
for all requests that you make using DynamicsWebApi starting from v.1.3.4
.
Please note, that id
field is not removed from the library, so all your existing scripts will work without any issue.
var request = {
key: "alternateKey='keyValue'",
collection: 'leads',
select: ['fullname', 'subject']
};
dynamicsWebApi.retrieveRequest(request).then(function (record) {
})
.catch(function (error) {
});
key
can be used as a primary key (id):
var request = {
key: '00000000-0000-0000-0000-000000000001',
collection: 'leads',
select: ['fullname', 'subject']
};
dynamicsWebApi.retrieveRequest(request).then(function (record) {
})
.catch(function (error) {
});
Making requests using Entity Logical Names
Starting from version 1.4.0, it is possible to make requests using Entity Logical Names (for example: account
, instead of accounts
).
There's a small perfomance impact when this feature is used outside CRM/D365 Web Resources: DynamicsWebApi makes a request to
Entity Metadata and retrieves LogicalCollectionName and LogicalName for all entities during the first call to Web Api on the page.
To enable this feature set useEntityNames: true
in DynamicsWebApi config.
var dynamicsWebApi = new DynamicsWebApi({ useEntityNames: true });
dynamicsWebApi.retrieve(leadId, 'lead', ['fullname', 'subject']).then(function (record) {
})
.catch(function (error) {
});
var request = {
collection: 'lead',
key: leadId,
select: ['fullname', 'subject']
};
dynamicsWebApi.retrieveRequest(request).then(function (record) {
})
.catch(function (error) {
});
This feature also applies when you set a navigation property and provide an entity name in the value:
var account = {
name: 'account name',
'primarycontactid@odata.bind': 'contact(00000000-0000-0000-0000-000000000001)'
}
dynamicsWebApi.create(account, 'account').then(function(accountId)){
}).catch(function (error) {
});
In the example above, entity names will be replaced with collection names: contact
with contacts
, account
with accounts
.
This happens, because DynamicsWebApi automatically checks all properties that end with @odata.bind
or @odata.id
.
Thus, there may be a case when those properties are not used but you still need a collection name instead of an entity name.
Please use the following method to get a collection name from a cached entity metadata:
var collectionName = dynamicsWebApi.utility.getCollectionName('account');
Please note, everything said above will happen only if you set useEntityNames: true
in the DynamicsWebApi config.
Using Proxy
Node.js Only. Starting from v.1.7.2 DynamicsWebApi supports different types of connections through proxy. To make it possible, I added two dependencies in a package.json
:
http-proxy-agent and https-proxy-agent, based on a type of a protocol, DynamicsWebApi
will use one of those agents.
In order to let DynamicsWebApi know that you are using proxy you have two options:
- add environmental variables
http_proxy
or https_proxy
in your .env file - or pass parameters in DynamicsWebApi configuration, for example:
const dynamicsWebApi = new DynamicsWebApi({
webApiUrl: 'https://myorg.api.crm.dynamics.com/api/data/v9.1/',
onTokenRefresh: acquireToken,
proxy: {
url: 'http://localhost:12345',
auth: {
username: 'john',
password: 'doe'
}
}
});
Using TypeScript Declaration Files
TypeScript declaration files d.ts
added with v.1.5.3.
If you are not familiar with declaration files, these files are used to provide TypeScript type information about an API that's written in JavaScript.
You want to consume those from your TypeScript code. Quote
Node.Js
If you are using Node.Js with TypeScript, declarations will be fetched with an NPM package during an installation or an update process.
At the top of a necessary .ts
file add the following:
import * as DynamicsWebApi from "dynamics-web-api";
Dynamics 365 web resource
If you are developing CRM Web Resources with TypeScript, you will need to download a necessary d.ts
file manually from the following folder: types.
As you may have noticed types
folder contains two declaration files: dynamics-web-api.d.ts
(Promises) and dynamics-web-api-callbacks.d.ts
(Callbacks) - download the one that you need.
Do not download both files! Otherwise you will have type declaration conflicts.
In my web resources project I usually put a declaration file under "./types/" folder. For example:
[project root]/
-- src/
-- form_web_resource.ts
-- types/
-- dynamics-web-api/
-- dynamics-web-api-callbacks.d.ts
-- tsconfig.json
Important! Make sure that you include types
folder in your tsconfig.json
:
"include": [
"./src/**/*",
"./types/**/*"
]
In Progress / Feature List
Many more features to come!
Thank you for your patience and for using DynamcisWebApi!
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.
Contributions
First of all, I would like to thank you for using DynamicsWebApi
library in your Dynamics 365 CE / Common Data Service project, the fact that my project helps someone to achieve their development goals already makes me happy.
And if you would like to contribute to the project you may do it in multiple ways:
- Submit an issue/bug if you have encountered one.
- If you know the root of the issue please feel free to submit a pull request, just make sure that all tests pass and if the fix needs new unit tests, please add one.
- Let me and community know if you have any ideas or suggestions on how to improve the project by submitting an issue on GitHub, I will label it as a 'future enhancement'.
- Feel free to connect with me on LinkedIn and if you wish to let me know how you use
DynamicsWebApi
and what project you are working on, I will be happy to hear about it. - I maintain this project in my free time and, to be honest with you, it takes a considerable amount of time to make sure that the library has all new features, gets improved and all raised tickets have been answered and fixed in a short amount of time. If you feel that this project has saved your time and you would like to support it, then please feel free to use PayPal or GitHub Sponsors. My PayPal button:
, GitHub button can be found on the project's page.
All contributions are greatly appreciated!