API Page / Query

Created by Arend-Jan Kauffmann, Described by Arend-Jan Kauffmann

Description

API pages are different from UI pages. They require different properties and don’t behave the same. Because API pages are used for integration with external applications, they should be treated as contracts. To achieve this, the following topics are important.

  • Separate API app
  • Page properties
  • Versioning
  • Field properties
  • Default fields

Separate API app

It’s a good practice to develop API pages in a separate app instead of combining them in a solution. By doing so, it provides better maintainability and is a good way of separation of concerns.

Page properties

An API page must define a minimum set of properties. Some of these properties will be part of the URL of the API endpoint. It is recommended to define the properties in the same order as they appear in the URL.

The properties that must be defined are:

  • PageType = API / QueryType = API
  • APIPublisher
  • APIGroup
  • APIVersion
  • EntitySetName
  • EntityName
  • DelayedInsert (only Page)
  • ODataKeyFields

APIPublisher

The name of the API publisher is usually the company creating the API. It is the first custom part in the URL for a given endpoint. The value is case insensitive.

Example:

APIPublisher = 'contoso';

APIGroup

Sets the group of the API endpoint that page or query is exposed in. In the URL the APIGroup comes after the APIPublisher. It can be used to distinguish different API apps or groups of APIs from each other. The value is case insensitive.

Example:

APIGroup = 'app1';

APIVersion

Sets the version(s) of the API endpoint the page or query is exposed in. This property is not mandatory. If it is not specified, then APIs will be exposed as version ‘beta’.

The APIVersion can be set to ‘beta’ or have the format ‘vx.y’. Example:

APIVersion = 'beta';

or

APIVersion = 'v1.0';

Multiple API versions

You should never break existing versions. Any breaking change requires to create a new version.

It is possible to expose an API in multiple versions:

APIVersion = 'beta', 'v1.0';

This allows to publish a new version of an API app without copying all individual objects and updating the version numbers. Only those API objects that are changed in a new version need to be copied. The other objects only need an addition to the APIVersion property to become available in the new version endpoint.

EntitySetName

The EntitySetName is the plural entity name. Think of it as the name of the collection of entities. It is recommended to use camelCasing for this property. The value is case sensitive!

Example:

EntitySetName = 'itemCategories';

EntityName

The EntityName sets the singular entity name for the API page or query. This name is not used in the URL. Instead, the EntityName is used in the metadata information. It is recommended to use camelCasing for this property.

Example:

EntityName = 'itemCategory';

DelayedInsert

This property is required on an editable API page. It does not apply to an API query object. If Editable = false is set on the API page, then DelayedInsert is not required. All APIs pages apply the behavior to first specify all field values and then insert the record at once.

Example:

DelayedInsert = true;

Full example

Together, the page properties look like:

PageType = API;
APIPublisher = 'contoso';
APIGroup = 'app1';
APIVersion = 'v1.0';
EntitySetName = 'itemCategories';
EntityName = 'itemCategory';
DelayedInsert = true;

The full url will look like: https://{url}/api/contoso/app1/v1.0/companies({id})/itemCategories

ODataKeyFields

The EntitySetName property in the URL can be extended with an identifier to indicate a single record.

Example:

.../itemCategories(768b6173-9b19-40ea-8e5d-ce181ec0d645)

The property ODataKeyFields defines which field(s) will be used for the identifier value. It is highly recommended to always use the SystemId field for this property. The SystemId field is immutable and will never change for a record.

The field that is defined in this property should be part of the API page.

Field properties

The base structure of an API page is similar to a UI list page:

layout
{
    area(Content)
    {
        repeater(records)
        {
            ...
        }
    }
}

When specifying the fields there are some considerations to keep in mind.

field(displayName; Rec.Name) { }

There are no mandatory properties. The property ApplicationArea does not play a role in API pages, so it can be skipped. The property Caption is also optional and should only be used in case the external application requires captions and the caption should be different from the standard caption as defined in the table.

The name of the field, in the example above displayName, should be defined in camelCasing. It may not contain spaces, dots, or other special characters.

It is common use to give certain fields a more describing name. Some examples are:

  • id for field SystemId
  • number for field “No.”
  • displayName for field Name

Mandatory fields

These fields should always be part of the API Page:

  • SystemId
    • This field should be exposed with the name id
  • SystemModifiedAt
    • This field should be exposed with the name lastModifiedDateTime. If you choose a different name, then the webhook functionality will not work properly.

Example:

layout
{
    area(Content)
    {
        repeater(records)
        {
            field(id; Rec.SystemId) { }
            field(lastModifiedDateTime; Rec.SystemModifiedAt) { }
        }
    }
}