We are living in a very connected world, all our devices have the ability to talk together and the same goes for our IT systems, so one of the things that pretty much every BC developer will face at one time or another is that someone needs to connect to a BC, luckily this is easy in BC so let us take a look at how we can accomplish this.
Exposing data
The first thing that we will cover is how to expose data in BC, you have two ways of exposing data in BC the first way is to publish either a page, query or XMLPort as a web service, to do this create the object that you wish to expose and go to web services in BC and add it.
However, there is a better way, and that is using API pages an API page is a regular page with some extra properties and it is created with the only goal of exposing data, another great thing about API pages is that you do need to publish API pages as web services because all pages of type API will automatically be exposed in BC, although we can also automatically publish web services we will get back to that later.
A simple API page could look like this:
page 50100 "API Items"
{
APIGroup = 'demo';
APIPublisher = 'fredborg';
APIVersion = 'v1.0';
ApplicationArea = All;
Caption = 'apiItems';
DelayedInsert = true;
EntityName = 'apiItem';
EntityCaption = 'apiItem';
EntitySetName = 'apiItems';
EntitySetCaption = 'apiItems';
PageType = API;
SourceTable = Item;
ODataKeyFields = SystemId;
Editable = false;
DataAccessIntent = ReadOnly;
layout
{
area(content)
{
repeater(General)
{
field(systemId; Rec.SystemId)
{
Caption = 'SystemId';
}
field(no; Rec."No.")
{
Caption = 'No.';
}
field(description; Rec.Description)
{
Caption = 'Description';
}
}
}
}
}
Besides setting the PageType to API the properties that you need to add are APIGroup, APIPublisher, APIVersion, EntityName, EntitySetName, and ODataKeyFields. All of these properties are used to call your API page except the ODataKeyFields, which is used as the unique key for your record, it is recommended to always use the SystemId as ODataKeyFields, because it will always be unique and the value will never change, another thing that is a good idea when creating API pages is to set the DataAccessIntent this will improve the performance of your application DataAccessIntent Property – Business Central | Microsoft Learn
Bound and UnBound Actions
Many times when we expose services to the world we also need to expose some functions, this can be done in two ways in BC we have what we call bound actions, which are actions that are bound to a certain record, these are the ones that we have on our API pages to add a bound action to our API page you must add [ServiceEnabled] above your function so a function could look like this.
[ServiceEnabled]
procedure UpdateUnitPrice(var actionContext: WebServiceActionContext; unitPrice: Decimal)
begin
rec."Unit Price" := unitPrice;
Rec.Modify();
actionContext.SetResultCode(WebServiceActionResultCode::Updated);
end;
An UnBound action is a procedure in a codeunit that can be called without having to do so on a record, so a simple UnBound action could look like this.
codeunit 50100 MyApiDemo
{
procedure myUnbound(itemDescription: text[50]; itemNo: Code[20])
var
Item: Record Item;
begin
Item.Get(itemNo);
Item.Description := itemDescription;
Item.Modify();
end;
}
Which looks like any other function, however when working with UnBound actions we must expose the codeuint as a web service, and this can be done by manually setting it up like I showed earlier or we can create an XML file that will automatically publish our web service when the extension is installed you can call the XML file anything you want I have called mine WS.xml and added this to the file.
<?xml version="1.0" encoding="utf-8"?>
<ExportedData>
<TenantWebServiceCollection>
<TenantWebService>
<ObjectType>CodeUnit</ObjectType>
<ObjectID>50100</ObjectID>
<ServiceName>MyApiDemo</ServiceName>
<Published>true</Published>
</TenantWebService>
</TenantWebServiceCollection>
</ExportedData>
Setting up OAuth
We have now created some API endpoints next we need to be able to call them, this is done by using Rest calls with OAuth as authentification, to set up OAuth, you must log in to your Azure Portal and go to App Registrations and create a new one:
Choose a name for your App registration choose single tenant and choose web as redirect and enter a redirect URL I have entered https://businesscentral.dynamics.com/OAuthLanding.htm in mine.
Press register, then go to API permissions and add a permission
And here you must add both business central permissions Delegated and Application, and under these also add all permissions.
Next press Grant admin consent
Now go to Certificates & secrets and create a new client secret
This will generate a new value and Secret ID copy the value and store it somewhere because you will not be able to see it again; next, go Overview and copy the Application (client) ID
Head over to BC and go to the page Azure Active Directory Applications and press new enter your Application (client) ID as the Client ID set the state to Enabled and set permissions I have given mine D365 ADMINISTRATOR and D365 BUS FULL ACCESS and then press Grant Consent.
Once this is done we can now start calling your API’s so head over to Postman create a GET request and enter the following URL https://api.businesscentral.dynamics.com/v2.0/{enviromentName}/api/microsoft/automation/v2.0/companies next head over to Authorization and choose OAuth 2.0
Then head down to Configure New Token and enter your information:
Access Token URL: https://login.microsoftonline.com/{TeantID}/oauth2/v2.0/token
Client ID: Is the Application (client) ID from Azure Portal
Client Secret: is the value of the Client secrets that you generated in Azure Portal (the one that you have to copy because you could only see it once )
Scope: Enter URL https://api.businesscentral.dynamics.com/.default
And press Get New Access Token
And choose Use token:
You should now be able to press send in Postman and get a list of your companies
Copy the id and try to change your URL so it looks like the following https://api.businesscentral.dynamics.com/v2.0/dev/api/fredborg/demo/v1.0/companies({companyID})/apiItems this should give you a list of all your items using your new API page
Next Copy the systemId of one of your items, and change the URL to the following https://api.businesscentral.dynamics.com/v2.0/dev/api/fredborg/demo/v1.0/companies({CompanyId})/apiItems({systemId})/Microsoft.NAV.updateUnitPrice and change the type to Post, and add the following to your body { “unitPrice”: 123456 } and press send:
And this will update the Unit Price of your Item OBS you might have noticed that my function name has been changed to start with lowercase, this is very important because even if your function starts with an uppercase in BC it will automatically be converted to lowercase in the API
Next, let us try to call our UnBound action to do this change the URL to this https://api.businesscentral.dynamics.com/v2.0/{teantId}/dev/ODataV4/MyApiDemo_myUnbound?company={companyId} and set the body to { “itemDescription”: “My Item”, “itemNo”: “1896-S” } and press send.
And this will update the Item Description of your Item.
And that is all there is to working with API’s in BC, until next time stay safe.