Dataverse Custom APIs are a powerful extension model of the PowerPlatform. However, at the time of writing, there are no direct method to execute a Custom API inside the PowerApps Canvas App model.
In this post I will show how to call any Custom API from a Canvas App with the use of a generic Power Automate Flow and the new ParseJSON functionality.
UPDATE 17-FEB-2023
Dataverse Custom APIs can now be called directly from PowerFx in Canvas Apps and Custom Pages. (still in experimental phase)
Call Dataverse actions directly in Power Fx | Microsoft Power Apps
Custom APIs in context
First off, I have to admit that I am die-hard fan of Dataverse Custom APIs since their introduction. The main reason is that it gives developers a way to use the same endpoint that the platform uses for the core functionalities (CRUD on the data tables etc..) and extend it with tailor-made messages that encapsulates server-side business logic over REST API requests.
As a result, the Custom APIs are agnostic to the caller and the same logic can be consumed from different sources like Dataverse plugins, Power Automate flows, model driven forms javascript, PCF controls, external programs etc…
Sadly, one of the blind spot is the direct use of Custom APIs within a Canvas Apps (or from the new Custom Pages) model. Let’s see how we can overcome this limitation in an indirect manner.
Lean more about Dataverse Custom APIs here : ๐Create and use Custom APIs (Microsoft Dataverse) - Power Apps | Microsoft Learn Don't forget to use my Custom API Manager tool for XrmToolBox for great authoring experience : ๐Dataverse Custom API Manager ยท XrmToolBox
Prerequisites
Follow these steps to setup the stage.
Enable ParseJson
The technique that I will show uses the Canvas Apps ParseJSON function that is still in preview, so the activation of the feature in the settings of the canvas app (or custom page) is required.
see the official documentation on the feature here ๐ParseJSON function in Power Apps (experimental) - Power Platform | Microsoft Learn
Install the PowerApps.Action.Runner solution (optional)
You can follow along and create your own Power Automate flows. But I have created a solution that contains the 2 Power Automate Flows shown later on.
- PowerApps:BoundActionRunner
- PowerApps:UnboundActionRunner
Here's the link to the github repo for download : ๐drivardxrm/PowerApps.Action.Runner: Generic Power Automate Flows to run Dataverse Custom APIs an Actions in Canvas Apps (github.com)
Surface the Flows in the Canvas Apps
Once the Power Automate Flows are installed, they need to be added to the Canvas App being edited by enabling them in the PowerAutomate pane of the Canvas App editor.
More on using PowerAutomate flows in a Canvas Apps ๐Use Power Automate pane - Power Apps | Microsoft Learn
Have some Custom APIs at hand
You can use any Custom API from your environment, but for the purpose of the demo, I will showcase 2 Custom APIs that can be found in my generic Dataverse Custom API collection.
Here's the link to the github repo for download : ๐drivardxrm/Dataverse-CustomApis: Collection of Dataverse Custom Apis (github.com)
For the Unbound Custom API example, I will use driv_GetTableInfo. This API takes for input a Dataverse table name (LogicalName) and return a collection of valuable information (metadata) on the table.
For the Bound Custom API example, I will show a Custom API bound to the systemuser table driv_GetUserTimezone. This API retrieves the Timezone of a given user from the user personal settings. It can be useful for datetime calculations.
Calling an unbound Custom API
Let’s start by presenting a Power Automate flow that can be used to call any unbound Custom APIs, PowerApps:UnboundActionRunner.
In fact the flow is quite simple and is merely a wrapper around the Custom API call. It consists of 3 parts, the trigger, the action and the response.
The Trigger
The flow is of type Instant/PowerApps and this type of trigger is what enables the PowerAutomate flow to be surfaced in the Canvas Apps editor as we seen before.
The configuration of the trigger defines 2 input parameters that will be supplied by the calling Canvas App
- ActionName : this expects the unique name of the Custom API
- InputJson : this expects a string in JSON format that contains the request parameters (inputs) one wishes to pass to the Custom API.
The Action
The action uses the Perform an unbound action that is part of the Power Automate Dataverse connector.
The Action Name selector exposes a list of all existing Custom API (and legacy Custom Actions) available in the environment, but in our case we’ll just select Enter custom value and affect the ActionName value defined earlier in the trigger.
Now, since the ActionName is dynamic and will only be resolved at runtime, the Power Automate engine cannot determine the individual input parameters so it shows an input field called Action Parameters that expects a JSON construct representing the input parameters of the API.
For example to call the GetTableInfo API on the account Table, the Action Parameters would look something like this
{
'LogicalName' : 'account'
}
The idea here is to set the Action parameters field with the InputJson received from the trigger. However, it’s really important here to convert the InputJson text value using the json() function otherwise an error will be thrown at runtime.
The Response
To pass the Custom API results back to the calling Canvas Apps, the flow makes use of the Respond to PowerAppp or flow action. Here, Simply set the value of OutputJson property to the response of the Custom API call made earlier.
outputs('Perform_an_unbound_action')?['body']
Calling the flow from a Canvas App
Now to the main event, let’s connect the dots and use the Flow inside a Canvas App (or Custom Page) to execute a Custom API.
The app shown takes a table name as input and at the click of the button, the PowerApps:UnboundActionRunner is used to execute the GetTableInfo Custom Api and output some metadata information on the form.
The OnSelect action of the button is used to set the value of the variable TableInfoResults. The value is the OutputJson resulting from the execution of PowerApps:UnboundActionRunner where the ActionName and InputJson of the Custom API are supplied. So on every click to the button the flow is executed, the Custom API is called and the expression is re-evaluated based on the value of the textbox.
Set(TableInfoResults,
'PowerApps:UnboundActionRunner'.Run(
"driv_GetTableInfo",
"{'LogicalName' : '" & txtInputTable.Value & "'}"
).outputjson)
The new ParseJSON() function is applied on the TableInfoResults variable to display the API results on the form. This will deserialize de output of the API response and expose its properties. Here, it’s important to cast the desired property to its correct object type (string = Text, number = Value etc..). (See the ParseJSON official docs)
As you can see below, the Custom API is being executed on each click of the button and the results are parsed and displayed on the form.
Calling a bound Custom API
To call a bound Custom API we will use the PowerApps:BoundActionRunner from my solution. The idea is the same as the unbound api version so I will not repeat everything.
The main differences are at the trigger level as more input parameters are expected.
- TableCollectionName : Collection Name of the Table (normaly the plural name)
- RecordId : uniqueidentifier (GUID) of the bound record
- ActionName : this expects the unique name of the Custom API
- InputJson : this expects a string in JSON format that contains the request parameters (inputs) one wishes to pass to the Custom API.
There’s also a minor difference in the way that the Action Name needs to be set on the Perform bound action step (Microsoft.Dynamics.CRM.{ActionName}).
Calling the flow from a Canvas App
For this demo, the button click will call the GetTimezoneInfo Custom API using the current user as the bound record.
Again, a variable is set (UserTimezoneResults) with the OutputJson received from the PowerApps:BoundActionRunner flow. The
Set(UserTimezoneResults,
('PowerApps:BoundActionRunner'.Run(
"systemusers",
First(Filter(Users,'PrimaryEmail'=User().Email)).User,
"driv_GetUserTimezone",
"")
.outputjson))
The same principle as earlier is applied to Parse the response of the flow and display the results on the form.
As you can appreciate, whenever the button is clicked the Custom API will be executed accordingly.
Takeaway
As cool as this is, I have to admit that I hope that this solution will have a short lifespan. It would be great if Custom APIs could be surfaced and executed directly inside Canvas Apps and Custom Pages.
In my opinion, the fact that we have to delegate the Custom API call to a PowerAutomate wrapper adds an unnecessary layer of complexity that can affect performance and maintainability.
Also, an important point to consider is the concurency on the flow and the API call limit. Since the flows are using a common connection to the Dataverse environment, this means that the same connection will be used for each execution. So if you have several apps used by hundreds of users all using the same flows and connections, you might run into problems down the road.
But hey, on the bright side of things let’s celebrate that with this solution Custom APIs can be unleashed in the realm of Canvas Apps. I see a lot of use cases for this.
Links
Photo by Pavan Trikutam on Unsplash
Be First to Comment