How to make a D365 API call from a Power Apps Portal

Dynamics 365’s Power Apps Portal functionality is incredibly useful for a wide variety of things, from internal intranets to allowing customers to submit information to Dynamics directly. A lot can be accomplished with them out of the box, but sometimes we need additional context from the Dynamics 365 API to finish a request submitted by a customer.

Learn more about Dynamics 365!

Learn More

Normally, from within Dynamics itself, this can be done with simple web requests. This process is a bit different on portals, however, as Dynamics’ security model prevents the portal from making requests directly to the API. Instead, we need to make a request to a record in the portal, which essentially makes the D365 API request on our behalf. To do so, we will need to create the following records, in order:

  • Web Template
  • Page Template
  • Web Page

Building the Web Template (including the FetchXML required for the API)

The Web Template will contain the FetchXML query, as well as some additional boilerplate that allows us to grab the values as needed via JavaScript elsewhere. As an example, here is a snippet that shows what we would put in this Web Template record if we wanted to get a list of accounts that match the provided name:

{% fetchxml feed %}

<fetch distinct="false" mapping="logical" output-format="xml-platform" version="1.0">

<entity name="account" >

<attribute name="accountid" />

<attribute name="name" />

<filter>

<condition attribute="name" operator="eq" value="{{request.params['name']}}" />

</filter>

</entity>

</fetch>

{% endfetchxml %} {

"results": [

{% for item in feed.results.entities %}

{

"accountId": "{{ item.accountid }}",

"accountName": "{{ item.name }}"

}

{% unless forloop.last %},{% endunless %}

{% endfor -%}

]

}

Let us dissect this for a moment, to illustrate what each component represents.  The FetchXML query can be copy-pasted from any existing query, e.g., XRMToolbox’s FetchXML Builder or an export from Dynamics.  This query will need to be modified if it needs dynamic parameters, however, with the following format:

="{{request.params['<parameter>']}}"

In the example above, we are sending a parameter called name as the value for a filter condition.

Below the FetchXML, we need to include some Liquid text that helps us parse the results.  Everything here is standard, except for the contents of the four loops. Each key-value pair represents the output of one field; the key is custom text that can be used in JavaScript, and the value simply points to an attribute in the FetchXML.  Each pair will have the following format:

"fieldname": "{{ item.fetchAttribute }}"

With all of this in mind, the Web Template should look something like the following:

image001

Adding a Page Template

Once the Web Template has been created, we now need to create a Page Template. This record just functions as a bridge between the web template (our code) and the web page (what we call via JavaScript).  All we need to do here is add the Website and change the Type to Web Template, so that we can select the template we created above.

image003

Creating the Web Page

The last configuration record we need to create is the Web Page, which we will call from JavaScript on to the portal.  We will need to fill in the following fields & values:

  • Name: The same as the Web Template and Page Template (for continuity)
  • Website: The website being used
  • Parent Page: Use the default top level page, which will be Home
  • Partial URL: Text you will use in the JavaScript request to refer to this page
  • Page Template: The template we created above
  • Publishing State: Published

If you have multi-language support enabled on the portal, the appropriate records will automatically be created.

Constructing the API Request in JavaScript

Once all these pieces are in place, we can finally make the request we need.  The request will look something like this:

const nameParameter = "Account Name";

$.getJSON("/fetchxml-b2baccount" + "?name=" + nameParameter, function (data) {

if (data.results.length > 0) {

// handle each record separately

data.results.forEach(function (element) {

const accountId = element.accountId;

const accountName = element.accountName;

// use the variables as you normally would in JS

});

}

});

The first part of the $.getJSON section is a reference to the Web Page we created, followed by standard query string parameter syntax to bring in the name filter we set up in the Web Template.  Everything else is standard JS.

Wrapping Things Up

The process of making an API call from Power Apps is more involved than making a similar request directly through Dynamics and is also a particularly useful tool when building customer-focused entity forms and web pages.  We have encountered multiple scenarios with our customers where this pattern is useful, and it has resulted in both better customer experience and better data integrity.  Some requirements simply are not possible without relying on the D365 API, so this feature really solidifies Power Apps Portals as a great solution for externally accessible web interactivity.