Using nestedFields to Audit Entry History
Liferay 7.4 U72+/GA72+
When you enable entry history for an object definition, you can use the nestedFields parameter with REST APIs to audit entry events. Add nestedFields=auditEvents to the path for GET requests (e.g., http://localhost:8080/o/c/tickets/?nestedFields=auditEvents).
To get started, set up a new Liferay 7.4 instance and prepare the provided tutorial code. Then, run the scripts to create entries, update them, and query those changes using the nestedFields parameter.
Using REST APIs to view an entry’s history requires both the View and Object Entry History permissions for the entry. See Permissions Framework Integration.
Setting Up a Liferay Instance
Start a new Liferay instance by running
docker run -it -m 8g -p 8080:8080 liferay/portal:7.4.3.132-ga132
Sign in to Liferay at http://localhost:8080. Use the email address test@liferay.com and the password test. When prompted, change the password to learn.
Next, enable Liferay’s persistent audit message processor. Then create and publish an object definition.
Enabling the Persistent Audit Message Processor
-
Open the Global Menu (
) → Control Panel tab, → System Settings. -
Under Security, click Audit and go to the Persistent Message Audit Message Processor tab.
-
Check Enabled.
-
Click Save.

Creating the Object Definition
-
Open the Global Menu (
), go to the Control Panel tab, and click Objects. -
Create an object draft.
Field Value Label AblePlural Label AblesName Able -
Add this field to the draft.
Label Field Name Type Required NamenameText ✔ -
In the Details tab, toggle Enable Entry History.
-
Publish the object.
Once published, you can access the object via Headless APIs.
Preparing the Sample Code
Download and unzip the sample code:
curl https://resources.learn.liferay.com/examples/liferay-g4m3.zip -O
unzip liferay-g4m3.zip
The sample code includes shell scripts for creating, updating, and querying object entries.
For a complete list of APIs generated for site and company objects, see Objects Headless Framework Integration. You can view and test custom object APIs via the Liferay API Explorer at [server]:[port]/o/api (e.g., localhost:8080/o/api). Click REST Applications and select an API.
Using the Sample Code
-
Navigate to the
curlfolder in theliferay-g4m3project.cd liferay-g4m3/curl -
Execute
Ables_POST_ToCompanyto create threeAbleentries../Ables_POST_ToCompany.sh{ ... "externalReferenceCode" : "able-one", "id" : 47512, ... "name" : "Able 1 - Foo" } { ... "externalReferenceCode" : "able-two", "id" : 47514, ... "name" : "Able 2 - Foo" } { ... "externalReferenceCode" : "able-three", "id" : 47516, ... "name" : "Able 3 - Foo" } -
Execute
Ables_PATCH_ByExternalReferenceCodewith theable-oneERC../Ables_PATCH_ByExternalReferenceCode.sh able-oneThis updates the entry’s name field twice.
{ ... "externalReferenceCode" : "able-one", "id" : 47512, ... "name" : "Able 1 - Bar" } { ... "externalReferenceCode" : "able-one", "id" : 47512, ... "name" : "Able 1 - Goo" } -
Execute
Ables_GET_ByExternalReferenceCodewith theable-oneERC../Ables_GET_ByExternalReferenceCode.sh able-oneThis returns the
able-oneentry with theauditEventsarray, a history of the entries events, beginning with the most recent event and ending with the entry’s creation.{ ... "auditEvents" : [ { "auditFieldChanges" : [ { "name" : "name", "newValue" : "Able 1 - Goo", "oldValue" : "Able 1 - Bar" } ], "dateCreated" : "2023-05-04T05:44:41Z", "eventType" : "UPDATE" }, { "auditFieldChanges" : [ { "name" : "name", "newValue" : "Able 1 - Bar", "oldValue" : "Able 1 - Foo" } ], "dateCreated" : "2023-05-04T05:44:40Z", "eventType" : "UPDATE" }, { "auditFieldChanges" : [ { "name" : "name", "newValue" : "Able 1 - Foo" } ], "dateCreated" : "2023-05-04T05:44:37Z", "eventType" : "ADD" } ], ... "externalReferenceCode" : "able-one", "id" : 47512, ... "name" : "Able 1 - Goo" }The
auditEventsarray includes these elements:auditFieldChanges: The updated field, with its new and old values.dateCreated: The time and date of the event.eventType: The event type (e.g.,ADD,UPDATE,DELETE).
-
Execute
Ables_GET_FromCompany../Ables_GET_FromCompany.sh able-oneThis returns all Able entries with an audit of their events, beginning with the most recent event.
{ ... "items" : [ { ... "auditEvents" : [ { "auditFieldChanges" : [ { "name" : "name", "newValue" : "Able 2 - Foo" } ], "dateCreated" : "2023-05-04T06:15:50Z", "eventType" : "ADD" } ], ... "externalReferenceCode" : "able-two", "id" : 47514, ... "name" : "Able 2 - Foo" }, { ... "auditEvents" : [ { "auditFieldChanges" : [ { "name" : "name", "newValue" : "Able 3 - Foo" } ], "dateCreated" : "2023-05-04T06:15:51Z", "eventType" : "ADD" } ], ... "externalReferenceCode" : "able-three", "id" : 47516, ... "name" : "Able 3 - Foo" }, { ... "auditEvents" : [ { "auditFieldChanges" : [ { "name" : "name", "newValue" : "Able 1 - Goo", "oldValue" : "Able 1 - Bar" } ], "dateCreated" : "2023-05-04T06:16:25Z", "eventType" : "UPDATE" }, { "auditFieldChanges" : [ { "name" : "name", "newValue" : "Able 1 - Bar", "oldValue" : "Able 1 - Foo" } ], "dateCreated" : "2023-05-04T06:16:24Z", "eventType" : "UPDATE" }, { "auditFieldChanges" : [ { "name" : "name", "newValue" : "Able 1 - Foo" } ], "dateCreated" : "2023-05-04T06:15:49Z", "eventType" : "ADD" } ], ... "externalReferenceCode" : "able-one", "id" : 47512, ... "name" : "Able 1 - Goo" } ], "lastPage" : 1, "page" : 1, "pageSize" : 20, "totalCount" : 3 }
Examining the GET Requests
These GET requests include the nestedFields=auditEvents parameter in their URLs.
Ables_GET_ByExternalReferenceCode
curl \
"http://localhost:8080/o/c/ables/by-external-reference-code/${1}?nestedFields=auditEvents" \
--user "test@liferay.com:learn"
Ables_GET_FromCompany
curl \
"http://localhost:8080/o/c/ables/?nestedFields=auditEvents" \
--user "test@liferay.com:learn"