Versioning with History Tables
Some applications require preserving previous versions of data when updates or changes occur. Using Tailor DB CDC, you can capture and log data modifications in real-time, storing prior record versions in a dedicated history table. This approach preserves historical data and enables change monitoring and analysis over time.
How to enable Data Versioning
- Create a history table
- Enable
PublishRecordEvents
settings - Add an event based trigger to
executor.cue
manifest
Example
1. Create a history table
Let's create a history table for the StockSummary
type to log its data changes.
<span><span style="color: var(--shiki-token-function)">resource</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"tailor_tailordb_type"</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"stock_summary_history"</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">workspace_id</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">tailor_workspace.ims.id</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">namespace</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">tailor_tailordb.ims.namespace</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">name</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"StockSummaryHistory"</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">description</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"StockSummaryHistory model"</span></span>
<span><span style="color: var(--shiki-color-text)"> </span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">fields:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">...</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">availableQuantity</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">type</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"float"</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">description</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"availableQuantity"</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">required</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">true</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">inStockQuantity</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">type</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"float"</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">description</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"DO NOT UPDATE FROM THE FRONT END. The quantity of the product in stock."</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">hooks</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">create_expr</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"decimal(_value.onHoldQuantity) + decimal(_value.availableQuantity)"</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">update_expr</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">create_expr</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">type_permission</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">local.permission_everyone</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
2. Enable PublishRecordEvents
settings
By enabling PublishRecordEvents
in the StockSummary
settings, you can create an event-based trigger that executes on every StockSummary
record update.
<span><span style="color: var(--shiki-token-function)">resource</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"tailor_tailordb_type"</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"stock_summary"</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">workspace_id</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">tailor_workspace.ims.id</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">namespace</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">tailor_tailordb.ims.namespace</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">name</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"StockSummary"</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">description</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"StockSummary model"</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">settings</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">publish_record_events</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">true</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">fields</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">...</span></span>
<span><span style="color: var(--shiki-color-text)"> } </span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">type_permission</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">local.permission_everyone</span></span>
<span><span style="color: var(--shiki-color-text)">} </span></span>
<span></span>
3. Add an event based trigger to executor.cue
manifest
The stockSummaryHistoryWithEvent
trigger executes on each StockSummary
record update.
It logs the old record by creating an entry in the history table.
<span><span style="color: var(--shiki-token-function)">resource</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"tailor_executor"</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"stockSummaryHistoryWithEvent"</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">workspace_id</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">tailor_workspace.ims.id</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">name</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"stockSummaryHistoryWithEvent"</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">description</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Create a data based on the event"</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">trigger</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">event</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">type</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"tailordb.type_record.updated"</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">operation</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">tailor_graphql</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">app_name</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">tailor_application.ims.name</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">invoker</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">event_user</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">true</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">query</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)"><<</span><span style="color: var(--shiki-color-text)">EOF</span></span>
<span><span style="color: var(--shiki-token-string)"> mutation ($input: StockSummaryHistoryCreateInput!) {</span></span>
<span><span style="color: var(--shiki-token-string)"> createStockSummaryHistory(input: $input) {</span></span>
<span><span style="color: var(--shiki-token-string)"> id</span></span>
<span><span style="color: var(--shiki-token-string)"> }</span></span>
<span><span style="color: var(--shiki-token-string)"> }</span></span>
<span><span style="color: var(--shiki-token-string)"> EOF</span></span>
<span><span style="color: var(--shiki-token-string)"> variables = <<EOF</span></span>
<span><span style="color: var(--shiki-token-string)"> ({</span></span>
<span><span style="color: var(--shiki-token-string)"> "variantID": args.oldRecord.variantID,</span></span>
<span><span style="color: var(--shiki-token-string)"> "onHoldQuantity": args.oldRecord.onHoldQuantity,</span></span>
<span><span style="color: var(--shiki-token-string)"> "availableQuantity": args.oldRecord.availableQuantity,</span></span>
<span><span style="color: var(--shiki-token-string)"> "totalCost": args.oldRecord.totalCost</span></span>
<span><span style="color: var(--shiki-token-string)"> })</span></span>
<span><span style="color: var(--shiki-token-string)"> EOF</span></span>
<span><span style="color: var(--shiki-token-string)"> }</span></span>
<span><span style="color: var(--shiki-token-string)"> }</span></span>
<span><span style="color: var(--shiki-token-string)">}</span></span>
<span></span>
Refer to the Event based trigger tutorial for detailed steps on setting up an event-based trigger. Once the trigger is configured, proceed with the following steps to confirm it is working correctly.
- Create a record in
StockSummary
table.
<span><span style="color: var(--shiki-token-keyword)">mutation</span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> createStockSummary(input: { </span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">variantID</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-string-expression)">"fc9349f5-2398-5aa6-a6a7-478c8db009ec"</span><span style="color: var(--shiki-color-text)">, </span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">onHoldQuantity</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-constant)">20</span><span style="color: var(--shiki-color-text)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">availableQuantity</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-constant)">20</span><span style="color: var(--shiki-color-text)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">totalCost</span><span style="color: var(--shiki-color-text)">:</span><span style="color: var(--shiki-token-constant)">800</span><span style="color: var(--shiki-color-text)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> }) {</span></span>
<span><span style="color: var(--shiki-color-text)"> id</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
- Update the record in
StockSummary
table.
<span><span style="color: var(--shiki-token-keyword)">mutation</span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> updateStockSummary(id:</span><span style="color: var(--shiki-token-comment)">"f701637c-12b1-4dab-ab45-3e952d8ff0b6"</span><span style="color: var(--shiki-color-text)"> input: { </span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">availableQuantity</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-constant)">60</span><span style="color: var(--shiki-color-text)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> }) {</span></span>
<span><span style="color: var(--shiki-color-text)"> id</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
Execute the following query to view the update history of the record in the history table.
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> stockSummaryHistories(query: { </span><span style="color: var(--shiki-token-string)">variantID</span><span style="color: var(--shiki-color-text)">: { </span><span style="color: var(--shiki-token-string)">eq</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-string-expression)">"fc9349f5-2398-5aa6-a6a7-478c8db009ec"</span><span style="color: var(--shiki-color-text)"> } }) {</span></span>
<span><span style="color: var(--shiki-color-text)"> edges {</span></span>
<span><span style="color: var(--shiki-color-text)"> node {</span></span>
<span><span style="color: var(--shiki-color-text)"> id</span></span>
<span><span style="color: var(--shiki-color-text)"> variantID</span></span>
<span><span style="color: var(--shiki-color-text)"> availableQuantity</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">"data"</span><span style="color: var(--shiki-color-text)">: {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">"stockSummaryHistories"</span><span style="color: var(--shiki-color-text)">: {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">"edges"</span><span style="color: var(--shiki-color-text)">: [</span></span>
<span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">"node"</span><span style="color: var(--shiki-color-text)">: {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">"id"</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-comment)">"323c9cff-a75f-45a5-aea5-33a367b4e0ce"</span><span style="color: var(--shiki-color-text)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">"variantID"</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-comment)">"fc9349f5-2398-5aa6-a6a7-478c8db009ec"</span><span style="color: var(--shiki-color-text)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">"availableQuantity"</span><span style="color: var(--shiki-color-text)">: 20</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>