Entity storage-Custom objects

Freshworks customers have real-world data modelling requirements, all of which cannot be met by a product’s native objects. The products offer custom fields to extend the existing native objects. At times, customers need entities that are related to native objects but do not exist within the product. Custom objects help meet this requirement. Through the use of an app, custom objects, and their relationships to native objects (associations) customers can create and use entities that act as part of the product itself, to meet specific functional requirements.

Apps that consume the default native objects or custom fields that the Freshworks products offer, can use the developer platform’s data storage feature. For a long time, the developer platform supported only key-value storage. This poses certain limitations in terms of key size, value field size, reverse look-ups, and capabilities to query, aggregate, report, and relate data. As part of the custom objects functionality, the developer platform offers entity storage in addition to the existing key-value storage. Entity storage helps provide capabilities such as reporting and aggregation of stored data, look up, retrieval and usage of relational information, querying on stored data, and so on.

To leverage the entity storage - custom objects feature, when you build an app, ensure to use FDK 8.2.0 or a later version.

Note:Custom objects created through an app (as specified in this section) cannot be accessed by another app.

To create and use custom objects,

  1. Define entities.
  2. As part of the app code, use the custom objects interface to create entity records.
  3. As part of the app code, define the necessary actions or operations on the entity records.

Sample use case: By leveraging the custom objects feature, employee information that an organization stores in an HRMS system can be brought into the developer platform’s entity storage, through a scheduled function that syncs both these systems. When an agent views a ticket on Freshservice, an app can look-up and relate the ticket requester to an employee whose details are stored in the entity storage. The app then retrieves this information for the agent.

For a demonstration of this use case, see Freshservice Custom Objects Data-sync App.

Notes:
  • Currently, Custom Objects does not support defining associations between custom and native objects.
  • You can build apps that use custom objects and submit the apps as a Freshworks app or Custom app. After review and approval, the Freshworks app is published to the marketplace. The published app or the custom app is available for installation. If you are unable to install a published app or publish a custom app, please contact us.
Define Entities

In Freshworks products, business data is modelled as objects. The objects are created out of specific object types. An entity is an object type. Entity definition is the schema definition of the object type. Schema definition includes the definition of all the object.attributes. Tickets, Requesters, Agents, Products, Vendors, and so on are some default native objects. tickets.description, tickets.status and tickets.priority are some of the attributes of the ticket entity.

You can use the custom objects feature to define new entities (object types). An object belonging to an entity (object type) is a record. Entity records (objects) of the defined object types can be created and an app can process these records to provide meaningful results.

To define and use entities:

  1. Define the custom object schema - Entity definition specification. The schema includes all object.attribute definitions.
  2. Initiate entity storage.
  3. Obtain a reference to the entity to facilitate schema and record operations.

Note: After an app is published, you can update or modify the entity definition and submit a new version of the app.
Entity definition specification

  1. From the app’s root directory, navigate to the config folder and create an entities.json file.
  2. In the entities.json file, use the following syntax to configure all custom objects that the app uses, as JSON objects.
    Notes:
    1. You can define a maximum of five entities for each business account.
    2. You can define a maximum of 20 fields (attributes) for each entity.

Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{ "<entity-name>": { "fields": [{ "name": "<attribute-name1>", "label": "<display-text>", "type": "<data type>", ... }, { "name": "<attribute-name2>", "label": "<display-text>", "type": "<data type>", "filterable": <true/false> }, ... ] }, "<entity-name1>": { "fields": [{ "name": "<attribute-name1>", "label": "<display-text>", "type": "<data type>" }, { "name": "<attribute-name2>", "label": "<display-text>", "type": "<data type>", "filterable": <true/false> }, ... ] } }
EXPAND ↓

File attribute Data type Description
<entity-name> object Schema definition of the custom object. An app can create records of type <entity-name> and process the records.
For example, for an app to create a custom object employees, <entity-name> in entities.json is employees.
The app and its users can create multiple records or objects of type employees. Each record is a collection of attributes. In entities.json, specify the attributes’ definition through the fields array.
fields
MANDATORY
Child attribute of <entity-name>
array of field objects All attributes of the custom object, specified as an array. Each array element contains a list of child-attributes that define the custom object.attribute.

Attributes of field object

Attribute name Data type Definition
name
MANDATORY

This is an alphanumeric field and can contain underscores. Must start with an alphabetic character.

string Identifier of the attribute.
When creating, programmatically storing objects, and querying data belonging to a specific entity (object type), ensure to use the exact fields.name that is specified in the schema.
label
MANDATORY
string Default display name for the attribute when it is exposed through a front-end component.

In the app files that render the app’s front-end components, if a different value is specified as the label for the input field, the values in the front-end files take precedence.

type
MANDATORY
string Data type of the attribute.
Possible values:
  • text: An attribute of this type accepts text data of upto 64 characters as input.

  • paragraph: An attribute of this type accepts long text data as input. paragraph type attributes are not filterable. Maximum possible input length is 2048 characters.

  • integer: An attribute of this type accepts long numeric whole number data as input. Maximum possible input length is 15 digits.

  • float: An attribute of this type accepts floating numeric data as input. Maximum possible input length is 15 digits.

  • datetime: An attribute of this type accepts ISO-8601 date format as input. datetime attribute types do not accept empty strings as input.

  • boolean: An attribute of this type accepts boolean data - true or false as input. boolean type attributes are not filterable. When exposed through a front-end component, the attribute is displayed as a checkbox.

  • enum: An attribute of this type accepts a single selection from a list of available options. When exposed through a front-end component, the attribute is displayed as a drop-down list and the options are displayed as list items. In entities.json, the fields.choices attribute contains the possible options of an attribute of the type enum.

  • date: An attribute of this type accepts a string of the YYYY-MM-DD format as input. date attribute types do not accept empty strings as input.

  • section: An attribute of this type groups fields. In entities.json, for an attribute of type section, the fields.fields attribute contains the details of all the fields grouped under the section.
    In the following example, a section labelled Branch contains:

    • A field labelled Branch name (text)
    • A field labelled Main office? (boolean)
    • A sub-section labelled Location (section)
    • Sub-section fields labelled: Branch city (text) and Branch country (text)
    Copied Copy
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    { "employees": { "fields": [{ "name": "employee_name", "label": "Name", "type": "text", "required": true }, { "name": "branch", "label": "Branch", "type": "section", "fields": [{ "name": "branch_name", "label": "Branch name", "type": "text" }, { "name": "branch_main_office", "label": "Main office?", "type": "boolean" }, { "name": "branch_location", "label": "Location", "type": "section", "fields": [{ "name": "branch_city", "label": "Branch city", "type": "text" }, { "name": "branch_country", "label": "Branch country", "type": "text" }] }] }] } }
    EXPAND ↓

    A section type attribute does not contribute to the number of fields but the non-section fields in a section contribute to the number of fields. For example, in the above entities.json sample there are five fields labelled Name, Branch name, Main office?, Branch city, and Branch country.

choices
Mandatory and valid for an attribute of type enum

Should not be an empty array
array of objects Options that populate the drop-down list of an attribute of type enum.
Attributes of the object:
id (integer): Numeric identifier of the option.
value (string): Actual option displayed in the drop-down list, when the attribute is exposed through a front-end component.
fields
Mandatory and valid for an attribute of type section
array of field objects Schema definition of all the fields grouped under a section or sub-section.
filterable boolean Specifies whether a subset of the stored records can be retrieved by specifying filter conditions for the attribute.
Notes:
  1. integer, text, datetime, date, and enum type attributes are filterable.
  2. You can define a maximum of five filterable fields in the schema definition.
Default value: false
required boolean Specifies whether the attribute is a mandatory attribute of the custom object.
When creating and updating records, ensure that the calls to the custom objects interface contain valid values for the attributes whose required value is true.
Note: An attribute of type section cannot be a mandatory attribute.
Default value: false

Sample entities.json Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
{ "employees": { "fields": [ { "name": "internal_id", "label": "Internal ID", "type": "integer", "required": true }, { "name": "first_name", "label": "First name", "type": "text", "filterable": true, "required": false }, { "name": "last_name", "label": "Last name", "type": "text", "filterable": true, "required": true }, { "name": "employee_id", "label": "Employee ID", "type": "text", "filterable": false, "required": false }, { "name": "employee_type", "label": "Employee type", "type": "enum", "choices": [ { "id": 1, "value": "Full Time" }, { "id": 2, "value": "Part Time" }, { "id": 3, "value": "Contract" }, { "id": 4, "value": "Internship" }, { "id": 5, "value": "Temporary" }, { "id": 6, "value": "Seasonal" }, { "id": 7, "value": "Voluntary" }, { "id": 8, "value": "Other" } ] }, { "name": "official_email", "label": "Work Email", "type": "text", "filterable": true, "required": true }, { "name": "terminated", "label": "Terminated?", "type": "boolean", "required": false }, { "name": "designation", "label": "Designation", "type": "text", "required": false }, { "name": "department", "label": "Department", "type": "text", "required": false }, { "name": "branch", "label": "Branch", "type": "section", "fields": [{ "name": "branch_name", "label": "Branch name", "type": "text" }, { "name": "branch_main_office", "label": "Main office?", "type": "boolean" }, { "name": "branch_location", "label": "Location", "type": "section", "fields": [{ "name": "branch_city", "label": "Branch city", "type": "text" }, { "name": "branch_country", "label": "Branch country", "type": "text" }] }] }, { "name": "synced_at", "label": "Last sync", "type": "datetime", "required": true }] }, "sync_history": { "fields": [ { "name": "sync_start", "label": "Sync start time", "type": "datetime", "filterable": true, "required": true }, { "name": "sync_end", "label": "Sync end time", "type": "datetime", "filterable": true, "required": true }, { "name": "num_synced_records", "label": "No. of records synced", "type": "integer", "required": true }, { "name": "domain", "label": "Domain", "type": "paragraph", "required": false }] } }
EXPAND ↓

Initiate entity storage

The developer platform’s data storage feature offers a client.db or $db interface to store and retrieve data. For custom objects, the interface is enhanced beyond its lightweight key-value storage capabilities to support entity storage. This facilitates custom object schema creation and deletion and CRUD operations on the records. Currently, the entity storage is accessible through a versioned interface that specifies that the app uses the v1 version of the entity storage feature.

To initiate entity storage and start using the custom objects interface, use the following constructor:

Sample frontend or client-side file Copied Copy
1
const entity = client.db.entity({ version: 'v1' });
Sample server.js Copied Copy
1
const $entity = $db.entity({ version: 'v1' });
Response

The constructor returns a versioned wrapper interface that can be used for custom objects operations.

Obtain a reference to the entity

To obtain a reference to the entity, use one of the following interface calls:

Copied Copy
1
2
const <entReference> = entity.get('<entity-name>'); const <entReference> = $entity.get('<entity-name>');
Sample frontend or client-side file Copied Copy
1
const employee = entity.get('employees');
Sample server.js Copied Copy
1
const employee = $entity.get('employees');
Response

A successful call returns a class that represents the entity and contains the static methods that can be used to perform operations on the entity records.

Copied Copy
1
2
3
4
5
6
7
8
{ schema: async function() {}, create: async function() {}, get: async function() {}, getAll: async function() {}, update: async function() {}, delete: async function() {} }
Create entities

The fdk validate and fdk pack commands validate the entities.json file, to ensure that the Entity Definition Specifications are appropriate. You can test entities creation and record operations before submitting the app.

App installation implicitly creates the entities specified in entities.json.

Retrieve entity schema

To verify the entity creation and view the schema of the created entity, use one of the following interface calls:

Note: <entReference> is a reference to the actual entity.
Copied Copy
1
2
<entReference>.schema(); $entity.get('<entity-name>').schema();
Sample front-end or client-side file Copied Copy
1
employees.schema();
Sample server.js Copied Copy
1
$entity.get('employees').schema();
Response

A successful call returns the entity object created based on entities.json specification, along with the following meta-data:

  • id: Unique numeric identifier of the entity, auto-generated by the developer platform when the entity is created and stored.
  • name: <entity-name> as specified in entities.json.
  • prefix: Prefix associated with the entity and used in internal interface calls to uniquely identify entities, auto-generated by the developer platform when the entity is created.

Sample response Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
{ "entity": { "id": 59126, "name": "employees", "prefix": "emplo", "fields": [{ "name": "internal_id", "label": "Internal ID", "type": "integer", "required": true }, { "name": "employee_id", "label": "Employee ID", "type": "text", "filterable": false, "required": false }, { "name": "employee_type", "label": "Employee type", "type": "enum", "choices": [ { "id": 1, "value": "Full Time" }, { "id": 2, "value": "Part Time" }, { "id": 3, "value": "Contract" }, { "id": 4, "value": "Internship" }, { "id": 5, "value": "Temporary" }, { "id": 6, "value": "Seasonal" }, { "id": 7, "value": "Voluntary" }, { "id": 8, "value": "Other" } ]}, { "name": "official_email", "label": "Work Email", "type": "text", "filterable": true, "required": true }, { "name": "terminated", "label": "Terminated?", "type": "boolean", "required": false }, { "name": "designation", "label": "Designation", "type": "text", "required": false }, { "name": "department", "label": "Department", "type": "text", "required": false }, { "name": "first_name", "label": "First name", "type": "text", "filterable": true, "required": false }, { "name": "last_name", "label": "Last name", "type": "text", "filterable": true, "required": true }, { "name": "branch", "label": "Branch", "type": "section", "fields": [ { "name": "branch_name", "label": "Branch name", "type": "text" }, { "name": "branch_main_office", "label": "Main office?", "type": "boolean" }, { "name": "branch_location", "label": "Location", "type": "section", "fields": [ { "name": "branch_city", "label": "Branch city", "type": "text" }, { "name": "branch_country", "label": "Branch country", "type": "text" } ] } ] }, { "name": "synced_at", "label": "Last sync", "type": "datetime", "required": true } ] } }
EXPAND ↓

For information on the fields array, see Attributes of field object.

Delete entities

App uninstallation clean slates data and implicitly deletes the entities definition.

Modify entity definition specification

After an app is published to the marketplace, you can modify the entity definition specification, test the changes, and resubmit a new version of the app.

In entities.json,

  • You can add new entities.
  • You can delete existing entities. All records associated with the deleted entity are deleted.
  • If you modify the <entity-name> of an existing entity, the entity with the previous <entity-name> is deleted and a new entity is created. All records associated with the previous <entity-name> are deleted.

In entities.json > <entities-name>.fields,

  • You can add a new field object.
  • You can delete an existing field object.
  • If you modify the fields.name value, the attribute with the previous name is deleted and a new attribute is created.
  • For an existing field, you cannot modify the fields.type value.
  • For an existing field, you cannot modify the fields.filterable value.
  • For an existing field, you can modify the fields.required value. If the fields.required value is modified to be true, for all existing entity records with no values for the field that is marked as required, the field value is stored as null.
  • For an attribute of the type enum, you cannot modify the fields.choices array.
  • For an attribute of the type section, you can add or delete new fields.

Define Record Operations

Through the app and its components, multiple records (objects) of a specific entity type can be created. These records provide data that can be processed by the app. As part of processing the data, the app can programmatically perform the following operations on the records:

The operations are performed through the custom objects interface, which returns promises. Successful calls return responses with requisite data and the following:

Response meta-data
  • display_id: Unique identifier of a record, auto-generated when the record is created. It is a combination of the prefix used to identify the entity and an incremental numeric value that uniquely identifies the record.
  • created_at: Timestamp of when the record is created, specified in the ISO-8601 format.
  • updated_at: Timestamp of when the record is last updated, specified in the ISO-8601 format.
Create a record

To create a record belonging to a specific entity, use one of the following interface calls:

Notes:
  1. There is no limitation on the number of records you can create for an entity. Currently, batch operations/bulk record creation is not supported.
  2. Each record can be of maximum 100 kb.
  3. <entReference> is a reference to the actual entity.

Copied Copy
1
2
3
4
<entReference>.create({ <fields.name1>: <valid value for fields.name1>, <fields.name2>: "<valid value for fields.name2>" });
For serverless apps Copied Copy
1
2
3
4
5
const record = await $entity.get(<entity-name>) .create({ <fields.name1>: <valid value for fields.name1>, <fields.name2>: "<valid value for fields.name2>" });
Sample front-end or client-side file Copied Copy
1
2
3
4
5
6
7
employee.create(newRecord) .then(function (data) { // Success message }) .catch(function (error) { // Error handling })
Sample payload (data) Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{ "internal_id": 6000122463, "first_name": "Peter", "last_name": "Parker", "employee_id": "E006", "employee_type": "Full Time", "official_email": "ppeter@freshteam.com", "terminated": false, "designation": "Sales Representative", "department": "6000060444", "branch": null, "branch_name": null, "branch_main_office": null, "branch_location": null, "branch_city": null, "branch_country": null, "synced_at": "2022-04-27T07:52:45.383Z" }
EXPAND ↓

Response: A successful create operation, returns the record object created based on the input and the corresponding meta-data. record.data contains the JSON object that is stored as the record text.

Sample response Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{ "record": { "display_id": "emplo-13", "created_time": "2022-04-27T07:52:47.266Z", "updated_time": "2022-04-27T07:52:47.266Z", "data": { "internal_id": 6000122463, "employee_id": "E006", "employee_type": "Full Time", "official_email": "ppeter@freshteam.com", "terminated": false, "designation": "Sales Representative", "department": "6000060444", "first_name": "Peter", "last_name": "Parker", "synced_at": "2022-04-27T07:52:45.383Z", "branch": null, "branch_name": "HQ", "branch_main_office": false, "branch_location": null, "branch_city": "Chennai", "branch_country": "India" } } }
EXPAND ↓
Update a record

To update a record belonging to a specific entity, use one of the following interface calls:

Notes:
  1. <entReference> is a reference to the actual entity. <display-id> is the unique identifier of a record, auto-generated when the record is created. <display-id> is returned as part of the response to a successful create record operation.
  2. Ensure that all attributes specified as required in entities.json are passed as part of the request call payload.

Copied Copy
1
2
3
4
<entReference>.update("<display-id>", { <fields.name1>: <valid value for fields.name1>, <fields.name2>: "<valid value for fields.name2>" });
For serverless apps Copied Copy
1
2
3
4
const record = await <entReference>.update(<display-id>, { <fields.name1 of the field to be updated>: <valid value for fields.name1>, <fields.name2>: "<valid value for fields.name2>" });
Sample app.js Copied Copy
1
2
3
4
employee.update("emplo-13", { "official_email": "lpeter@freshteam.com", "last_name": "Larker", });

Response: A successful update operation, returns the record object updated based on the input and the corresponding meta-data. record.data contains the JSON object that is stored as the updated record text.

Sample response Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{ "record": { "display_id": "emplo-13", "created_time": "2022-04-27T07:52:47.266Z", "updated_time": "2022-04-28T07:52:47.266Z", "data": { "internal_id": 6000122463, "employee_id": "E006", "employee_type": "Full Time", "official_email": "lpeter@freshteam.com", "terminated": false, "designation": "Sales Representative", "department": "6000060444", "first_name": "Peter", "last_name": "Larker", "synced_at": "2022-04-27T07:52:45.383Z", "branch": null, "branch_name": "HQ", "branch_main_office": false, "branch_location": null, "branch_city": "Chennai", "branch_country": "India" } } }
EXPAND ↓
Retrieve a record

To retrieve a specific record belonging to a specific entity, use one of the following interface calls:

Note: <entReference> is a reference to the actual entity. <display-id> is the unique identifier of a record, auto-generated when the record is created. <display-id> is returned as part of the response to a successful create record operation.

Copied Copy
1
<entReference>.get("<display-id>");
For serverless apps Copied Copy
1
const record = await $entity.get('<entity-name>').get('<display-id>');
Sample front-end file Copied Copy
1
employee.get("emplo-13");

Response: A successful retrieve by display-id operation, returns the retrieved record object and the corresponding meta-data. record.data contains the JSON object that is stored as the record text.

Sample response Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{ "record": { "display_id": "emplo-13", "created_time": "2022-04-27T07:52:47.266Z", "updated_time": "2022-04-28T07:52:47.266Z", "data": { "internal_id": 6000122463, "employee_id": "E006", "employee_type": "Full Time", "official_email": "lpeter@freshteam.com", "terminated": false, "designation": "Sales Representative", "department": "6000060444", "first_name": "Peter", "last_name": "Larker", "synced_at": "2022-04-27T07:52:45.383Z", "branch": null, "branch_name": "HQ", "branch_main_office": false, "branch_location": null, "branch_city": "Chennai", "branch_country": "India" } } }
EXPAND ↓
Retrieve all records

To retrieve all records belonging to a specific entity, use one of the following interface calls:

Note: <entReference> is a reference to the actual entity.
Copied Copy
1
<entReference>.getAll();
For serverless apps Copied Copy
1
const records = await <entReference>.getAll({});
Sample front-end file Copied Copy
1
2
3
4
5
6
7
8
9
function loadEmployees() { employee.getAll() .then(function (data) { //render details of all employees as a list }) .catch(function (error) { //error message }) }
EXPAND ↓

Response: A successful retrieve all records operation, returns the retrieved records as an array of objects. The retrieved records are not paginated and are retrieved in sets of 100 records. The links attribute in the response provides a token to retrieve the next set of records.

Sample response Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{ "records": [{ "display_id": "emplo-13", "created_time": "2022-04-27T07:52:47.266Z", "updated_time": "2022-04-28T07:52:47.266Z", "data": { "internal_id": 6000122463, "employee_id": "E006", "employee_type": "Full Time", "official_email": "lpeter@freshteam.com", "terminated": false, "designation": "Sales Representative", "department": "6000060444", "first_name": "Peter", "last_name": "Larker", "synced_at": "2022-04-27T07:52:45.383Z", "branch": null, "branch_name": "HQ", "branch_main_office": false, "branch_location": null, "branch_city": "Chennai", "branch_country": "India" } }, { "display_id": "emplo-14", "created_time": "2022-04-27T07:52:47.266Z", "updated_time": "2022-04-27T07:52:47.266Z", "data": { "internal_id": 6000122464, "employee_id": "E007", "employee_type": "Full Time", "official_email": "llouis@freshteam.com", "terminated": false, "designation": "Sales Representative", "department": "6000060444", "first_name": "Louis", "last_name": "Lee", "synced_at": "2022-04-27T07:52:45.383Z", "branch": null, "branch_name": "SF II", "branch_main_office": false, "branch_location": null, "branch_city": "San Francisco", "branch_country": "US" } }], "links": { "next": { "marker": "Lbr2zerj3WHNDsZ1NsdFj7NiigDlittVkGc7RmPjKF3" } } }
EXPAND ↓
Response attributes

Attribute name Data type Description
records array of objects All retrieved records belonging to a specific entity, specified as an array of objects. Each array element is an object with the following attributes:
  • display_id: Unique identifier of a record, auto-generated when the record is created. It is a combination of the prefix used to identify the entity and an incremental numeric value that uniquely identifies the record.
  • created_at: Timestamp of when the record is created, specified in the ISO-8601 format.
  • updated_at: Timestamp of when the record is last updated, specified in the ISO-8601 format.
  • data: JSON object that is stored as the record text.
links link object

Link to the next set of records.

Attribute of the link object:
next (object): Identifier of the next set of records, in the following key: value format:
Copied Copy
1
marker: "<identifier string>"
If there is only one set of records, the marker value is null.

Sample call to retrieve a succeeding set of records Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
function loadEmployees() { employee.getAll({ next: { marker: "Lbr2zerj3WHNDsZ1NsdFj7NiigDlittVkGc7RmPjKF3" }}) .then(function (data) { //render details of all employees as a list }) .catch(function (error) { //error message }) }
EXPAND ↓

Response: The marker value, in the call, is an encoded token of a record id. A successful call retrieves the next set of records, starting from the record identified by the marker value. The marker value of the last set of records is null.

Apply filters and retrieve specific records

You can include queries as part of the retrieve all records call and thereby specify filter criteria for the filterable fields. On successful processing of the call only specific records that satisfy the criteria are retrieved.

To specify queries as part of the interface call, use the following format:

Notes:
  1. When filtering by a datetime field, ensure that the filter criteria is specified in the ISO-8601 format. Range querying on datetime fields is currently not supported.
  2. <entReference> is a reference to the actual entity.

Copied Copy
1
2
3
4
5
<entReference>.getAll({ query: { <filterable field-name>: "<filter criteria value>" } });
For serverless apps Copied Copy
1
2
3
4
5
const record = await <entReference>.getAll({ query: { <filterable field-name>: "<filter criteria value>" } });

To use the and or or operations to construct a query with multiple query parameters, use the following samples:

Notes:
  1. When constructing a query with multiple query parameters, a minimum of two query parameters and a maximum of three filterable fields or query parameters should be used.
  2. In an and operation, ensure that the attributes (filterable fields) used are different.
  3. In an or operation, ensure that the attributes (filterable fields) used are the same.
  4. Nested queries are not supported.

Sample 1 - $or query construct
Front-end file Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const record = employee.getAll({ query: { $or: [ { last_name: "Parker" }, { last_name: "Larker" } ] } }).then(function(data){ // access to filtered employees }).catch(function(data){ // Handle errors })
EXPAND ↓
server.js Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
const record = await employee.getAll({ query: { $or: [ { last_name: "Parker" }, { last_name: "Larker" } ] } });
EXPAND ↓

Response: A successful retrieve all records operation with filters, returns the records that meet the filter criteria as an array of objects.

Sample response Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{ "records": [{ "display_id": "emplo-13", "created_time": "2022-04-27T07:52:47.266Z", "updated_time": "2022-04-28T07:52:47.266Z", "data": { "internal_id": 6000122463, "employee_id": "E006", "employee_type": "Full Time", "official_email": "lpeter@freshteam.com", "terminated": false, "designation": "Sales Representative", "department": "6000060444", "first_name": "Peter", "last_name": "Larker", "synced_at": "2022-04-27T07:52:45.383Z", "branch": null, "branch_name": "HQ", "branch_main_office": false, "branch_location": null, "branch_city": "Chennai", "branch_country": "India" } }], "links": { "next": { "marker": "Lbr2zerj3WHNDsZ1NsdFj7NiigDlittVkGc7RmPjKF3" } } }
EXPAND ↓
Response attributes

Attribute name Data type Description
records array of objects

All records belonging to a specific entity and meeting the filter criteria, specified as an array of objects. Each array element is an object with the following attributes:

  • display_id: Unique identifier of a record, auto-generated when the record is created. It is a combination of the prefix used to identify the entity and an incremental numeric value that uniquely identifies the record.
  • created_at: Timestamp of when the record is created, specified in the ISO-8601 format.
  • updated_at: Timestamp of when the record is last updated, specified in the ISO-8601 format.
  • data: JSON object that is stored as the record text.

links link object

Link to the next set of records.

Attribute of the link object:
next (object): An identifier of the next set of records, specified in the following key: value format: Copied Copy
1
marker: "<identifier string>"
To access the next set of records, use the same interface call along with the same query and specify the marker as shown in Sample 2.

Sample 2- retrieve subsequent sets of filtered records Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const record = await employee.getAll({ query: { $or: [ { last_name: "Parker" }, { last_name: "Larker" } ] }, next: { marker: "Lbr2zerj3WHNDsZ1NsdFj7NiigDlittVkGc7RmPjKF3" } });
EXPAND ↓
Sample 3 - $and query construct Copied Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const record = await $entity.get(employees).getAll({ query: { $and: [ { last_name: "Larker" }, { official_email: "lpeter@freshteam.com" } ] }, next: { marker: "Lbr2zerj3WHNDsZ1NsdFj7NiigDlittVkGc7RmPjKF3" } });
EXPAND ↓
Delete a record

To delete a specific record belonging to a specific entity, use one of the following interface calls:

Note: <entReference> is reference to the actual entity. <display-id> is the unique identifier of a record, auto-generated when the record is created. <display-id> is returned as part of the response to a successful create record operation.

Copied Copy
1
<entReference>.delete("<display-id>");
For serverless apps Copied Copy
1
const record = await <entReference>.delete("<display-id>");
Sample front-end file Copied Copy
1
employee.delete("emplo-14");
Response: A successful delete operation returns an empty object.
Error Responses

If a call fails, the custom objects interface returns an error response similar to following sample:

Copied Copy
1
2
3
4
5
6
7
8
9
10
11
{ "message": "Invalid input field", "status": 400, "errors": [ { "message": "Field name should be of type string", "name": "label" } ], "errorSource": "APP" }
EXPAND ↓
Error response attributes

Attribute name Data type Description
message string Generic message specifying the reason for the error.
status number HTTP status code.
errors array of error objects All errors that caused the call to fail, specified as an array.
Attributes of the error object:
  • message (string): Specific validation error.
  • name (string): Name of the entity field whose validation failed.
errorSource string Specifies whether the error is app or platform related.
Possible values: APP, PLATFORM

Test

Note: To test your app, use the latest version of the Chrome browser

  1. To test the configured custom objects, from the command prompt navigate to the app project folder and run the following command:
    Copied Copy
    1
    fdk run

    The command validates the entities.json file and displays the validation errors, if any. Fix the validation errors and run the command. The command creates a .sqlite file and the entities that are defined in entity.json. .sqlite mimics the platform’s entity storage, in the local setting.

    Note:If the entity definition specification in entities.json is modified, for the modification to reflect in the local .sqlite file, you will have to rerun the fdk run command. A prompt to resync is displayed and a resync deletes all existing entities, associated data, and records and creates a clean instance.
  2. Log in to your Freshservice account.

  3. To the Freshservice account URL, append ?dev=true.
    Example URL: https://subdomain.freshservice.com/helpdesk/tickets/1?dev=true

    If the app is successfully created, it is rendered in the app location specified in manifest.json.

  4. To test the custom objects interface calls, from the app, simulate record operations. If the calls fail, appropriate error responses are displayed.