Dynamics 365 Business Central: handling BLOB fields on APIs

I’m not a big fan of using BLOB fields on the Dynamics 365 Business Central database, but there are many scenarios where you need to handle BLOBs inside the database directly.

As an example, imagine to have an application that handles different types of labels related to items and these labels are stored inside the Dynamics 365 Business Central database in a table called Labels. This table is defined as following:

table 50105 Label
{    
  DataClassification = CustomerContent;    
  Caption = 'Label';
    
  fields    
  {        
    field(1; "Item No."; Code[20])        
    {            
      DataClassification = CustomerContent;            
      Caption = 'Item No.';        
    }        
    field(2; Description; Text[50])        
    {            
      DataClassification = CustomerContent;            
      Caption = 'Description';        
    }        
    field(3; Type; Enum "Label Type")        
    {            
      DataClassification = CustomerContent;            
      Caption = 'Type';        
    }        
    field(4; Data; Blob)        
    {            
      DataClassification = CustomerContent;            
      Caption = 'Data';        
    }    
 }
    
 keys    
 {        
   key(PK; "Item No.")        
   {            
     Clustered = true;        
   }    
 }
}

The data field is a BLOB field that contains the binary data of the label. Then I have a page called Label List that permits you to manage the label definition and to generate the label binary data dynamically (I’ve created two demo data here, where the data BLOB field contains a binary text exactly like the content of the label description for better readability and simplicity):

The Load Demo Data action populates the BLOB field by calling the LoadDemoData method defined in the following codeunit:

codeunit 50109 LabelManagement
{
   procedure LoadDemoData(var Label: Record Label)
   var
      OutStr: OutStream;
      MsgData: Label 'Label Data inserted.';
   begin
      Label.Data.CreateOutStream(OutStr);
      OutStr.WriteText(Label.Description);
      Label.Modify();
      Message(MsgData);
   end;
}

Now imagine that you want to expose the label’s data to external applications. As a best practice, you should use APIs for this scope. So, we create an API page that shows all the label’s data as needed (yes, also the data BLOB field). The API page is defined as follows:

page 50106 LabelAPI
{
    PageType = API;
    Caption = 'labelApi';
    APIPublisher = 'sd';
    APIGroup = 'customapi';
    APIVersion = 'v1.0';
    EntityName = 'label';
    EntitySetName = 'labels';
    SourceTable = Label;
    DelayedInsert = true;
    ODataKeyFields = SystemId;
    InsertAllowed = false;
    DeleteAllowed = false;
    layout    {
        area(Content)
        {
            repeater(GroupName)
            {
                field(itemNo; "Item No.")
                {
                    Caption = 'itemNo';
                }
                field(description; Description)
                {
                    Caption = 'description';
                }
                field(type; Type)
                {
                    Caption = 'type';
                }
                field(data; Data)
                {
                    Caption = 'data';
                }
            }
        }
    }
}

Nothing strange until here I think. But what happens when you call this API from an external application to read the label’s data?

I see that many partners think that a simple GET to the /labels API endpoint permits you to retrieve all the labels data (standard fields + BLOB data field) and seeing the response format of the GET request seems like a problem in the API definition because you don’t have the BLOB data. Nothing more wrong! BLOB fields are not directly exposed via APIs and I think this is absolutely correct. Imagine to have a lots of label records and that every record has a 20MB of binary data attached. By calling the /labels API could return a big amount of data and the network transaction will not be so efficient. So, how can I retrieve the BLOB data of a label entity?

When you use a BLOB field inside an API page and you perform a GET http call to the API, as a response you have all the entity simple fields and two new urls:

  • YourBLOBField@odata.mediaReadLink: link to read the BLOB data for this entity
  • YourBlobField@odata.mediaEditLink: link to update the BLOB data for this entity

As an exampe, this is the response I receive from a call to the /labels API endpoint in my demo application:

So, how to retrieve the BLOB data for the label called ITEM002? Just perform a GET request to the data@odata.mediaReadLink url and this is the response:

We have the content of the BLOB field.

How to update the label’s data BLOB field via API? You need to perform a PUT request to the data@odata.mediaEditLink url like the following:

PUT {BASEURL}/api/sd/customapi/v1.0/companies(COMPANYID)/labels('ITEM002')/data 
Content-Type: application/x-www-form-urlencoded
Authorization: Basic USERNAME WS_ACCESS_KEY
If-Match: *

{   
   "data": "label modified via API"
}

In this PUT request, you need to include an If-Match header and in the request body you need to pass the new content of your BLOB field. This is the response of this request:

As you can see, the BLOB content is updated.

Quite easy I think if you remember these details… 😉

2 Comments

  1. In BC15 we have persistent blob in the System Application. It’s not really documented, but I guess the goal is to collect blob values in one ace instead of mixing them up with tables. Anyway, when you would use this new persistent blob, then the API page would need some tweaking I think.

    Liked by 1 person

  2. I am just wondering. Can I configure a similar custom API in PowerAutomate as custom connector to receive a file content from say sharepoint. Do you have a similar work?

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.