CDS pluginCDS plugintypeInstalls into a CAP project and extends the runtime. CAP loads it through cds.requires or the plugin hook.See every one →What this type is →by SAPby SAPoriginPublished by SAP itself. A visible facet, never an input to a reading — a dormant official plugin reads exactly like a dormant independent one.See every one →

@cap-js/change-tracking

Out-of-the-box change history for annotated entities.

Facts

Latestv2.2.3ReleasedLast pushLicenceApache-2.0

Where it sits

Lives in
Provides
Change trackingCAPChange trackingcapabilityKeeping a readable history of what changed on a record, who changed it and when — as business history rather than database journalling.
Flavors
Node.jsfrom its runtime
Requires
@sap/cds≥ 8.5@sap/cdsartifactThe peer range, as published.

Who is behind it

SAP Cloud Application Programming ModelSAP Cloud Application Programming ModelOrganisationMaintains this
In the atlas: maintains 34
@cap-js
maintainerAkshay AvinashPersonContributes to this
In the atlas: contributes to 1
@akshay11298
Bob den OsPersonContributes to this
In the atlas: contributes to 13
@BobdenOs
Christian GeorgiPersonContributes to this
In the atlas: maintains 1, contributes to 53
@chgeo
Christian SchüringsPersonContributes to this
In the atlas: contributes to 25
@cschuerings
Daniel HutzelPersonContributes to this
In the atlas: contributes to 32, wrote 3
@danjoa
Frank HufnagelPersonContributes to this
In the atlas: contributes to 8
@FrankVisuals
Tomas RimkusPersonContributes to this
In the atlas: contributes to 4
@hakimio
JocelynGu135PersonContributes to this
In the atlas: contributes to 1
@JocelynGu135
Johannes VogelPersonContributes to this
In the atlas: contributes to 25
@johannes-vogel
Simon KoblerPersonContributes to this
In the atlas: contributes to 6
@KoblerS
22 contributors

Before choosing this

Conditions

LicenceSAP Developer License Agreement 3.2 CAP§ 1, 2, 3A cds-plugin is used with SAP CAP materials, or an artifact is explicitly identified as a CAP plugin or application.
  • § 1 License. Productive customer applications are limited to SAP BTP or another SAP-licensed platform, and mass extraction to a non-SAP product is restricted except where solely required for SAP interoperability.
  • § 2 Intellectual Property. The agreement retains SAP's rights in the covered materials and limits making APIs, tools or software available to third parties, while preserving ownership of customer applications subject to SAP's underlying rights.
  • § 3 Artificial Intelligence Training. The agreement prohibits use of the covered materials and customer applications for training artificial-intelligence models or systems.
SAP Developer License Agreement 3.2 CAP →

What it declares

Every artifact

Type
CDS plugin
Origin
By SAP
Runtime
Node.js

As a CDS plugin

Declared for
>=8.5
Distributed through
npm

Connections1

Install

npm add @cap-js/change-tracking

Derived from the package name. No recipe recorded; the README has the configuration.

Learn more1

Readings
  1. New and improved: Change Tracking pluginrupamaity, SAP Community
What changed
  1. v2.0.0

    Changes are captured by database triggers generated for SQLite, SAP HANA, PostgreSQL and H2 instead of by event handlers. The sap.changelog.ChangeLog entity is folded into sap.changelog.Changes, where entityID is renamed objectID and serviceEntityPath and keys are removed, and the considerLocalizedValues option is gone.

    Makes stale:

    Queries, views or annotations that read sap.changelog.ChangeLog or the entityID column, and configurations that set considerLocalizedValues.

README

quoted from cap-js/change-tracking under Apache-2.0, read
cap-js/change-tracking/README.mdOn GitHub

Change Tracking Plugin for SAP Cloud Application Programming Model (CAP)

A CDS plugin for automatic capturing, storing, and viewing of change records for modelled entities.

REUSE status

Important

With version 2.0, we completely refactored how changes are tracked. Previously, the logic relied on the application layer, which limited the types of trackable queries and came with major performance penalties in larger projects. With v2.0 the changes are now fully tracked on the database layer via database triggers. Furthermore, the table definition for changes was cleaned up with v2.0. This means any upgrade involves a schema change.

Why This Plugin Works for Both CAP Node.js and CAP Java

This plugin is implemented as a CDS plugin — a Node.js package that hooks into CDS's build pipeline. During the build, it generates native database triggers (SQL DDL) directly from the CDS model. Because the triggers run at the database level, they are completely independent of the application runtime: neither CAP Node.js nor CAP Java needs to do anything special at request time to capture changes.

The annotations (@changelog) are interpreted once at build time by the plugin, and the resulting triggers are then deployed alongside the schema. Any application — regardless of runtime — that writes to the tracked tables will have its changes captured automatically by the database.

Table of Contents

Try it Locally

To enable change tracking, simply add this self-configuring plugin package to your project and add the @changelog annotation to your data model, as explained in the Detailed Explanation.

npm add @cap-js/change-tracking

Alternatively, a full sample application is provided in the tests/bookshop folder:

git clone https://github.com/cap-js/change-tracking
cd change-tracking
npm i
cd tests/bookshop
cds watch

For testing with CAP Java, just execute mvn cds:watch instead of cds watch.

[!Warning]

Please note that if your project is multi-tenant, then the CDS version must be higher than 8.6 and the mtx version higher than 2.5 for change-tracking to work.

[!Warning]

When using multi-tenancy with MTX, the generated database triggers, facets and associations have to be created by the model provider of the MTX component. Therefore, the plugin also must be added to the package.json of the MTX sidecar.

Annotations

Warning

Please be aware that sensitive or personal data (annotated with @PersonalData) is not change tracked, since viewing the log allows users to circumvent audit-logging.

All you need to do is identify what should be change-tracked by annotating respective entities and elements in your model with the @changelog annotation. Following the best practice of separation of concerns, we do so in a separate file db/change-tracking.cds:

using { sap.capire.Incidents, sap.capire.Conversations } from './schema.cds';

annotate Incidents {
  customer @changelog: [customer.name];
  title    @changelog;
  status   @changelog;
}

annotate Conversations with @changelog: [author, timestamp] {
  message  @changelog @Common.Label: 'Message';
}

The minimal annotation we require for change tracking is @changelog on elements, as for the elements title and status in the sample snippet above.

Additional identifiers or labels can be added to obtain more human-readable change records as described below.

Testing

With the steps above, we have successfully set up change tracking for our reference application. Let's see that in action.

  1. Start the server:
cds watch
  1. Make a change on your change-tracked elements. This change will automatically be persisted in the database table (sap.changelog.Changes) and made available in a pre-defined view, namely the Change History view for your convenience.
Change History View
change-history

If you have a Fiori Element application, the CDS plugin automatically provides and generates a view sap.changelog.ChangeView, the facet of which is automatically added to the Fiori Object Page of your change-tracked entities/elements. In the UI, this corresponds to the Change History table which serves to help you to view and search the stored change records of your modeled entities.

Detailed Explanation

Human-readable Types and Fields

By default the implementation looks up Object Type names or Field names from respective @title or @Common.Label annotations and uses the technical name as a fall back.

For example, without the @title annotation, changes to conversation entries would show up with the technical entity name:

change-history-type

With an annotation, and possible i18n translations like so:

annotate Incidents.conversations with @title: '{i18n>CONVERSATION}';

We get a human-readable display for Object Type:

change-history-type-hr
Human-readable IDs

The changelog annotations for Object ID are defined at entity level.

Having a @changelog annotation without any additional identifiers, changes to conversation entries show up as simple entity IDs:

change-history-id

However, this is not advisable and the readability can be increased with an explicit object ID as follows:

annotate Incidents.conversation with @changelog: [author, timestamp];
change-history-id-hr

The annotation accepts a list of paths, meaning the following examples are all possible as well:

type CustomType : String;

extend Customers with elements {
  note: CustomType
}

annotate Incidents with @changelog: [
  title, customer.note, urgency.name
];
annotate Incidents with @changelog: [
  customer.address.city, customer.address.streetAddress, status.criticality
] {
  title    @changelog;
}
ObjectID fallback for NULL values

When some @changelog fields are null, the object ID shows <empty> for the missing values:

annotate Orders with @changelog: [customer, region];

If region is null, the objectID would display for example as ACME Corp, <empty>. When all @changelog fields are null, the object ID falls back to the entity's primary key.

Human-readable Values

The changelog annotations for New Value and Old Value are defined at element level.

They are already human-readable by default, unless the @changelog definition cannot be uniquely mapped such as types enum or Association.

For example, having a @changelog annotation on Incident's customer field without any additional identifiers, changes would show up as UUIDs:

customer @changelog;
change-history-value

Hence, here it is essential to add a unique identifier to obtain human-readable value columns:

customer @changelog: [customer.name];
change-history-value-hr
Human-readable IDs for Composition Entries

When a child entity is modified, a composition changelog entry is created on the parent entity with valueDataType = 'cds.Composition'. The Object ID on this entry identifies what was affected. How this Object ID is resolved depends on whether the composition is a composition of one or a composition of many.

Composition of one

For composition of one, the Object ID is derived from the child entity's @changelog annotation. If the child entity has no @changelog, it falls back to the parent entity's @changelog. Any @changelog annotation on the composition field itself is not considered for the Object ID.

@changelog: [name]
entity BookStores {
  key ID   : UUID;
  name     : String;
  registry : Composition of one BookStoreRegistry @changelog: ('Not considered');
}

@changelog: [code]
entity BookStoreRegistry {
  key ID      : UUID;
  code        : String;
  validOn     : Date @changelog;
}

When validOn is changed on BookStoreRegistry, the composition entry on BookStores will have objectID = 'TEST-REG' (from the child's @changelog: [code]), not the parent's name.

Composition of many

For composition of many, the Object ID is not derived from the child entity, since different children can have different identifiers. Instead, it falls back to the parent entity's @changelog annotation.

@changelog: [name]
entity BookStores {
  key ID : UUID;
  name   : String;
  books  : Composition of many Books on books.bookStore = $self @changelog;
}

@changelog: [title]
entity Books {
  key ID    : UUID;
  bookStore : Association to BookStores;
  title     : String @changelog;
}

When a Books entry is modified, the composition entry on BookStores will have objectID = 'Shakespeare and Company' (from the parent's @changelog: [name]).

You can customize the Object ID on the composition field using @changelog with a path or an expression. The annotation must only reference elements from the parent entity, not from the child entity.

Path-based:

annotate BookStores with @changelog: [name] {
  books @changelog: [name];  // uses parent's 'name' as Object ID
}

Expression-based:

annotate BookStores with @changelog: [name] {
  books @changelog: ('Books from ' || name);  // evaluates to e.g. 'Books from Shakespeare and Company'
}

Note

When multiple children are created or deleted in a single transaction, only one composition entry is created per parent. The modification type on composition parent entries is always update, regardless of whether children were created, updated, or deleted. This is because a single transaction may involve multiple types of operations on different children, making it impossible to assign a single accurate modification type. The actual modification type is reflected on the child entries themselves.

Results in the following change logs: Change Tracking Composition of many children

Expression-based labels

In addition to plain paths, the @changelog annotation supports CDS expressions for computing human-readable labels. Expressions must be wrapped in parentheses () to distinguish them from paths:

annotate Incidents {
  status @changelog: (status.code || ': ' || status.descr);
  price  @changelog: (price < 100 ? 'Budget' : 'Premium');
}

When status changes from N (New) to R (Resolved), the label would show "N: New" and "R: Resolved" instead of raw key values. For price, a ternary expression classifies the value into a human-readable category.

Localized values

If a human-readable value is annotated for the changelog, it will be localized.

extend Incidents with elements {
  status: Association to one Status @changelog: [status.descr];
}

entity Status {
  key code: String(1);
      descr: localized String(20);
}

By default the value label stored for the change is localized in the language of the user who caused the change. Meaning if a German speaking user changes the status, the human-readable value would be by default in German.

In cases, like above, where the human-readable value only consists of one field, targets a localized property and goes along the (un-)managed association, a dynamic human-readable value is used, meaning if an English-speaking user looks at the changes, the value label will be shown in English, for a French-speaking user in French and so on.

Tracing any kind of change

Change tracking is implemented with Database triggers and supports HANA Cloud, SQLite and Postgres.

Leveraging database triggers means any change will be tracked no matter how it is represented in the service. Thus tracking changes made via unions, or via views with joins will still work.

Tracking datetime fields with a fixed time zone

The plugin supports tracking datetime field changes when the field has a time zone annotated.

extend Incidents with elements {
  closedAt : DateTime @changelog @Common.Timezone : 'Europe/Berlin';
  openedAt : DateTime @changelog @Common.Timezone : openedTimeZone;
  openedTimeZone : String @Common.IsTimezone;
}

In both cases the plugin will show the annotated time zone for change values in changes for the two fields. In the second case the time zone is dynamically fetched and modifications to the time zone field will also reflect in the change records for that field.

Advanced Options

Altered table view

The Change History view can be easily adapted and configured to your own needs by simply changing or extending it. For example, let's assume we only want to show the first 5 columns in equal spacing, we would extend db/change-tracking.cds as follows:

using from '@cap-js/change-tracking';

annotate sap.changelog.ChangeView with @(
  UI.LineItem : [
    { Value: modificationLabel },
    { Value: createdAt },
    { Value: createdBy },
    { Value: entityLabel },
    { Value: objectID }
  ]
);

In the UI, the Change History table now contains only the five columns with the desired properties:

change-history-custom

For more information and examples on adding Fiori Annotations, see Adding SAP Fiori Annotations.

Disable lazy loading

To disable the lazy loading feature of the Change History table, you can add the following annotation to your db/change-tracking.cds:

using from '@cap-js/change-tracking';

annotate sap.changelog.aspect @(UI.Facets: [{
  $Type : 'UI.ReferenceFacet',
  ID    : 'ChangeHistoryFacet',
  Label : '{i18n>ChangeHistory}',
  Target: 'changes/@UI.PresentationVariant',
  @UI.PartOfPreview
}]);

The system now uses the SAP Fiori elements default setting @UI.PartOfPreview: true, such that the table will always be shown when navigating to that respective Object page.

Disable UI Facet generation

If you do not want the auto-provided UI facet for viewing changes, you can provide your own facet for the changes association in the @UI.Facets annotation and the plugin won't override it.

Furthermore if you annotate the association as not readable, the facet is also not added. You can achieve this, like

@Capabilities.NavigationRestrictions.RestrictedProperties : [
  {
    NavigationProperty : changes,
    ReadRestrictions : {
      Readable : false,
    },
  },
]
entity SalesOrders {
  key ID : Int16;
      title  : String @changelog;
}
Disable Association to Changes Generation

For some scenarios, e.g. when doing UNION and the @changelog annotation is still propagated, the automatic addition of the association to changes does not make sense. You can use @changelog.disable_assoc for this to be disabled on entity level.

Important

This will also suppress the addition of the UI facet, since the change-view is no longer available as the target entity.

Disabling Change Tracking

When @changelog is defined on the underlying DB entity, all services that expose the entity will automatically track changes due to annotation propagation. Use @changelog: false at the service, entity, or element level to selectively opt out of tracking.

// db/schema.cds — tracked across all services by default
@changelog: [title]
entity Incidents : cuid, managed {
  title    : String @changelog;
  date     : Date @changelog;
}
// srv/services.cds
service ProcessorService {
  entity Incidents as projection on db.Incidents;
}

// Disable tracking for the entire admin service
@changelog: false;
service AdminService {
  entity Incidents as projection on db.Incidents;
}

// Disable tracking for a specific projection only
service ReportingService {
  @changelog: false
  entity IncidentStats as projection on db.Incidents;
}

// Disable tracking for a specific element only
annotate ProcessorService.Incidents with {
  date @changelog: false;
}
Select types of changes to track

If you do not want to track some types of changes, you can disable them using disableCreateTracking, disableUpdateTracking and disableDeleteTracking configs in your project settings:

{
  "cds": {
    "requires": {
      "change-tracking": {
        "disableCreateTracking": true,
        "disableUpdateTracking": false,
        "disableDeleteTracking": true
      }
    }
  }
}
Preserve change logs of deleted data

By default, deleting a record will also automatically delete all associated change logs. This helps reduce the impact on the size of the database. You can turn this behavior off globally by adding the following switch to the package.json of your project

"cds": {
  "requires": {
    "change-tracking": {
      "preserveDeletes": true
    }
  }
}

Important

Preserving the change logs of deleted data can have a significant impact on the size of the change logging table, since now such data also survives automated data retention runs. You must implement an own data retention strategy for the change logging table in order to manage the size and performance of your database.

Adjust the depth of the entity hierarchy tracking

By default, the depth of the changes hierarchy for any entity is 3. This means, its changes as well as the changes of its compositions and the compositions of its compositions are shown on the UI.

"cds": {
  "requires": {
    "change-tracking": {
      "maxDisplayHierarchyDepth": 3
    }
  }
}

Important

The depth of the hierarchy has a performance impact, so be careful with increasing it!

Disable Children Change Linking

By default, when a child entity in a composition is change-tracked, an additional changelog is created on the parent entity. This record has valueDataType = 'cds.Composition' and the attribute set to the composition field name (e.g. conversation). It links changes from a child to the parent and allows hierarchical viewing of changes across the composition tree.

To disable this linking behavior for a specific composition, annotate the composition field with @changelog: false:

entity Incidents : cuid, managed {
  title : String @changelog;
  @changelog: false
  conversation : Composition of many {
    key ID    : UUID;
    timestamp : type of managed:createdAt;
    author    : type of managed:createdBy;
    message   : String @changelog;
  };
}

With @changelog: false on the composition field, individual changes on the child entity (e.g. changes to message) are still tracked, but no linking changelog is created on the parent. This means the child's changes will not appear in the parent's change history hierarchy.

Examples

This section describes modelling cases for further reference, from simple to complex, including the following:

Tracing Changes

Use cases for tracing changes

Use Case 1: Trace the changes of child nodes from the current entity and display the meaningful data from child nodes (composition relation)

Modelling in db/schema.cds

entity Incidents : managed, cuid {
  ...
  title          : String @title: 'Title';
  conversation   : Composition of many Conversation;
  ...
}

aspect Conversation: managed, cuid {
    ...
    message   : String;
}

Add the following @changelog annotations in db/change-tracking.cds

annotate Incidents with @changelog: [title] {
  conversation @changelog;
}

annotate Conversation with @changelog: [message] {
  message @changelog;
}

When a Conversation entry is modified, the composition changelog entry on Incidents will automatically use the child's Object ID derived from Conversation @changelog: [message]. This way, the change history on the parent shows which conversation was affected.

Use Case 2: Trace the changes of associated entities from the current entity and display the meaningful data from associated entities (association relation)

Modelling in db/schema.cds

entity Incidents : cuid, managed {
  ...
  customer       : Association to Customers;
  title          : String @title: 'Title';
  ...
}

entity Customers : cuid, managed {
  ...
  email          : EMailAddress;
  ...
}

Add the following @changelog annotations in db/change-tracking.cds

annotate Incidents with @changelog: [title] {
  customer @changelog: [customer.email];
}
Use Case 3: Trace the changes of chained associated entities from the current entity and display the meaningful data from associated entities (association relation)

Modelling in db/schema.cds

entity Incidents : cuid, managed {
  ...
  title          : String @title: 'Title';
  customer       : Association to Customers;
  ...
}

entity Customers : cuid, managed {
  ...
  address : Composition of one Addresses;
  ...
}

Add the following @changelog annotations in db/change-tracking.cds

annotate Incidents with @changelog: [title] {
  customer @changelog: [customer.address.city, customer.address.streetAddress];
}

Change-tracking supports analyzing chained associated entities from the current entity in case the entity in consumer applications is a pure relation table. However, the usage of chained associated entities is not recommended due to performance cost.


🛑 Don'ts
Use Case 1: Don't trace changes for field(s) with Association to many
entity Customers : cuid, managed {
  ...
  incidents : Association to many Incidents on incidents.customer = $self;
}

The reason is that: the relationship: Association to many is only for modelling purpose and there is no concrete field in database table. In the above sample, there is no column for incidents in the table Customers, but there is a navigation property of incidents in Customers OData entity metadata.

Use Case 2: Don't trace changes for field(s) with Unmanaged Association
entity AggregatedBusinessTransactionData @(cds.autoexpose) : cuid {
    FootprintInventory: Association to one FootprintInventories
                        on  FootprintInventory.month                      = month
                        and FootprintInventory.year                       = year
                        and FootprintInventory.FootprintInventoryScope.ID = FootprintInventoryScope.ID;
    ...
}

The reason is that: When deploying to relational databases, Associations are mapped to foreign keys. Yet, when mapped to non-relational databases they're just references. More details could be found in Prefer Managed Associations. In the above sample, there is no column for FootprintInventory in the table AggregatedBusinessTransactionData, but there is a navigation property FootprintInventory in OData entity metadata.

Troubleshooting

Modifying records directly on the database fails after upgrading to version 2

The database triggers for change tracking leverage session context to retrieve the user id needed for the createdBy field of the change record.

On HANA session_context('APPLICATIONUSER') is being used. All queries going through the CAP database driver @cap-js/hana will have the context. In HANA Cloud Cockpit it is set to the logged in user as well.

On Postgres current_setting('cap.applicationuser', true) is being used to retrieve the user information and again all queries going through @cap-js/postgres have the context.

On SQLite no session context function exists. Thus @cap-js/sqlite defines a UDF called session_context which looks up the session context from the database client used by @cap-js/sqlite. If you manually insert to SQLite circumventing the CAP driver, please make sure the UDF is defined.

"Invalid RID address" error with statement-level HANA triggers

Statement-level triggers could cause "invalid RID address" errors on certain HANA versions. This issue has been fixed with HANA version 2026.2.17 (QRC 1/2026). If you encounter this error, upgrade your HANA instance to at least this version.

As a temporary workaround, you can fall back to row-level triggers by setting the rowLevelTriggers flag in your project configuration:

"cds": {
  "requires": {
    "change-tracking": {
      "rowLevelTriggers": true
    }
  }
}

Note

Row-level triggers are slower than statement-level triggers, especially for bulk operations. The rowLevelTriggers flag is a temporary workaround and will be removed in a future version.

"no such table" error when starting locally with a persistent SQLite database

When using a file-based SQLite database (e.g. db.sqlite) for local development, starting the server may fail with an error like:

Error: no such table: main.SAP_CAPIRE_BOOKSHOP_BOOKS in:
CREATE TRIGGER IF NOT EXISTS SAP_CAPIRE_BOOKSHOP_BOOKS_ct_create AFTER INSERT ...

CAP only auto-deploys the schema at server startup for in-memory SQLite. For a persistent SQLite file, you must deploy the schema yourself before starting the server, otherwise the change-tracking plugin cannot create its triggers on the (missing) application tables:

cds deploy
cds watch

See the CAP docs on using persistent SQLite databases for details.


Contributing

This project is open to feature requests/suggestions, bug reports etc. via GitHub issues. Contribution and feedback are encouraged and always welcome. For more information about how to contribute, the project structure, as well as additional contribution information, see our Contribution Guidelines.

Code of Conduct

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone. By participating in this project, you agree to abide by its Code of Conduct at all times.

Licensing

Copyright 2023 SAP SE or an SAP affiliate company and contributors. Please see our LICENSE for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available via the REUSE tool.

Apache-2.0, read

History

Latest

v2.2.3

Features 1, Fixes 3

Notes
Added
  • Support tracking on localized text tables. Changes to annotated localized fields (e.g. title, descr, locale) are linked to the parent entity's change history.
Fixed
  • cds deploy --to h2 --dry outputs valid SQL without prepending the H2 trigger generation log
  • The trigger Java code for H2 is now using the applicationuser session context variable for the createdBy column.
  • The trigger Java code for H2 is now using the now session context variable to have consistent createdAt values throughout the given transaction.
  1. v2.2.2

    Fixes 1

    Notes
    Fixed
    • Cast the code list key to string in dynamic localization to fix SqlError: invalid number in service-level ChangeView when using HANA
  2. v2.2.1

    Changed 1, Fixes 1

    Notes
    Changed
    • Generate a dedicated Java class per H2 trigger implementation and a shared AbstractChangeTrackingTrigger base class instead of inline trigger logic
    Fixed
    • Preserve v1 transaction grouping during migration by deriving transactionID from changeLog_ID instead of the seconds of createdAt
  3. v2.2.0

    Fixes 5

    Notes
    Fixed
    • Prevent /changes navigation from leaking change history for change-tracked fields that are excluded from a service projection
    • Consider @changelog: false on nested composition-of-many targets during deep writes
    • Fix Duplicate definition of element "parent_entityKey" (in entity:"sap.changelog.ChangeView"/query:1) compile error during tenant upgrade in multi-tenant apps with extensibility: true
    • Fix HANA SqlError: invalid number / invalid DATE, TIME or TIMESTAMP value when reading the Fiori change hierarchy caused by casting valueChanged*Label instead valueChanged* in ChangeView
    • Fix trigger generation for H2 for local CAP Java development
  4. v2.1.0

    Fixes 5

    Notes
    Fixed
    • Prevent SQLite sidecar crash on startup in multi-tenant applications due to trigger/index deployment running before tenant tables exist
    • Invoke enhanceModel in after-deploy handler of cds.xt.DeploymentService because the loaded handler is not invoked on the tenant CSN from ModelProviderService.getCsn()
    • Fix HDI deployment failure (sap.changelog.Changes_CT_INDEX.hdbindex requires db://SAP_CHANGELOG_CHANGES which is not provided by any file) caused by adding .hdbindex artifacts even when Changes table does not exist in the compiled model
    • HDI deployment failure (invalid column name: CHANGEVIEW_0.PARENT_ENTITYKEY) during extensibility upgrades since enhanceModel was not called on the tenant CSN before HANA compilation
    • Support model enhancement with xtended CSN flavor to support extensibility and feature toggle setups
  5. v2.0.3

    Fixes 1

    Notes
    Fixed
    • Skip trigger generation for localized text tables (*.texts)
  6. v2.0.2

    Fixes 3

    Notes
    Fixed
    • Fix <expr>.cds compilation error during cds deploy --to hana when expression-based @changelog annotations (CXL) are used as objectIDs on entities with compositions
    • Prevent @PersonalData field values from leaking into objectID or valueChangedToLabel columns when @changelog annotations reference such fields via path expressions or CXL
    • Fix missing cds.Composition parent entry and broken parent_ID links in generated triggers when a DB-level select * view of a parent entity inherits the same composition
  7. v2.0.1

    Fixes 1

    Notes
    Fixed
    • Resolve false "Conflicting @changelog annotations" error when a service projection flattens association and inherits plain true instead of a specific @changelog annotation value (e.g., [author.name]) due to CDS annotation propagation
  8. v2.0.0dated in the atlas

    Removed 4, Features 12, Changed 7, Fixes 7

    Notes
    Added
    • Trigger generation for SQLite, HANA, Postgres and H2 to perform change tracking on the database level
    • Allow grouping of changes via new transactionID column
    • valueChangedFromLabel and valueChangedToLabel columns on sap.changelog.Changes for localized labels
    • Allow change tracking skip via session variables for entire transactions, entities, elements (ct.skip)
    • Dynamic localized label lookup: if a @changelog label points to a localized property from a code list entity, the label is resolved in the reader's locale at read time
    • Support CDS expression language (CXL) in @changelog annotations for custom objectIDs and changelog labels
    • Customizable objectID for composition changelog entries on parent entities
    • Support for tracked Date, DateTime, Time and Timestamp properties with correct formatting
    • Support for @Common.Timezone on change-tracked entities
    • Database indexes on sap.changelog.Changes for faster parent composition lookups and deduplication queries
    • Generation of .hdbmigrationtable and updating undeploy.json via cds add change-tracking-migration
    • HANA procedure SAP_CHANGELOG_RESTORE_BACKLINKS to restore parent-child hierarchy for composition changes
    Changed
    • Display changes with a Tree Table
    • Changes from child entities are shown on the parent ChangeView by default (configurable via maxDisplayHierarchyDepth)
    • Composition changelog entries on parents always have update as modification type
    • Only skip change tracking for @PersonalData.IsPotentiallySensitive and @PersonalData.IsPotentiallyPersonal (not @PersonalData.FieldSemantics)
    • Localization is performed on database level via sap.changelog.i18nKeys table
    • Modifications on sap.changelog.Changes:
      • Removed serviceEntityPath, keys and foreign key changeLog
      • Renamed entityID to objectID
      • Added entityKey, createdAt and createdBy from deleted entity sap.changelog.ChangeLog
      • Added parent association and children composition
    • Change History section is hidden in draft mode
    Fixed
    • Performance issues when working with entities that include a large number of fields and children
    • Creation of changelogs at bulk operations
    • Search and sort functionality on the ChangeView
    • LargeString values are truncated and don't lead to failing inserts
    • Explicit type casts for Date, DateTime, Time, Timestamp and Decimal fields in ChangeView
    • Format tracked decimal values with correct precision (e.g., Decimal(11,4) stores 0 as 0.0000)
    • ObjectID correctly falls back to entity key when all @changelog fields are NULL
    Removed
    • Event handler registration for change capture mechanism
    • Entity sap.changelog.ChangeLog and flattened into sap.changelog.Changes
    • Removed configuration option considerLocalizedValues
    • Information about which service a change originated from
  9. v1.1.4

    Fixes 2

    Notes
    Fixed
    • Server no longer crashes when after a DB migration the service name or attribute name change
    • Fix crash when applications uses feature toogles or extensibility
  10. v1.1.3

    Changed 1

    Notes
    Changed
    • Correctly handle changes on foreign keys when sending them via the document notation on an API level.
  11. v1.1.2

    Fixes 1

    Notes
    Fixed
    • Support single keys which are not named ID
  12. v1.1.1

    Features 2, Changed 1

    Notes
    Added
    • Allow tracking of localized values with considerLocalizedValues
    • Added more translations for the UI labels for more languages
    Changed
    • Correct localisation for cds.Date, cds.Time, cds.DateTime and cds.Timestamp properties
  13. v1.1.0

    Features 2, Changed 1, Fixes 3

    Notes
    Added
    • License entry
    • Added translations for the UI labels for more languages
    Fixed
    • Handling of multiple records in one request
    • Handle cases where the key contains '/'
    • Instantiate the changes association correctly so it does not impact other @cap-js plugins
    Changed
    • Prepare for CDS9 in tests
  14. v1.0.8

    Features 3, Changed 4, Fixes 7

    Notes
    Added
    • Added @UI.MultiLineText to value fields
    • Added support for Multi-Tenancy
    • Added configuration options to disable tracking of CREATE/UPDATE/DELETE operations on a project level
    Fixed
    • Handling of numeric and boolean fields was faulty, when an initial value of 0 for numeric or false for boolean was supplied
    • Decimal values were handled differently for HANA and SQlite
    • Missing UI Label for one attribute (ChangeLog.ID) of the Changes UI facet
    • Support for @UI.HeaderInfo.TypeName as fallback for the UI Label of the key
    • Compilation error when an association is used as a key
    • Fixed handling of unmanaged composition of many
    • Proper casing of the operation enum type
    Changed
    • Added warning and mitigation for multi-tenant deployments with MTX
    • Added a disclaimer of upcoming new version having a minimum requirement of CDS 8.6 for multitenancy fix
    • Changed the default limit on non-HANA databases from 255 to 5000 characters for all String values
    • Updated peer dependency from CDS7 to CDS8
  15. v1.0.7

    Features 2, Changed 3, Fixes 8

    Notes
    Added
    • A global switch to preserve change logs for deleted data
    • For hierarchical entities, a method to determine their structure and a flag to indicate whether it is a root entity was introduced. For child entities, information about the parent is recorded.
    Fixed
    • CDS 8 does not support queries for draft-enabled entities on the application service anymore. This was causing: SqliteError: NOT NULL constraint failed: (...).DraftAdministrativeData_DraftUUID
    • CDS 8 deprecated cds.transaction, causing change logs of nested documents to be wrong, replaced with req.event
    • CDS 8 rejects all direct CRUD requests for auto-exposed Compositions in non-draft cases. This was affecting test cases, since the ChangeView falls into this category
    • req._params and req.context are not official APIs and stopped working with CDS 8, replaced with official APIs
    • When running test cases in CDS 8, some requests failed with a status code of 404
    • ServiceEntity is not captured in the ChangeLog table in some cases
    • When modeling an inline entity, a non-existent association and parent ID was recorded
    • Fixed handling, when reqData was undefined
    Changed
    • Peer dependency to @sap/cds changed to ">=7"
    • Data marked as personal data using data privacy annotations won't get change-tracked anymore to satisfy product standards
    • Restructured Documentation
  16. v1.0.6

    Features 1, Changed 1, Fixes 3

    Notes
    Fixed
    • Storage of wrong ObjectID in some special scenarios
    • Missing localization of managed fields
    • Views without keys won't get the association and UI facet pushed anymore
    Added
    • A method to disable automatic generation of the UI Facet
    Changed
    • Improved documentation of the @changelog Annotation
  17. v1.0.5

    Fixes 1

    Notes
    Fixed
    • Error on HANA when logging Boolean or Numeric Data
  18. v1.0.4

    Features 1, Changed 1, Fixes 2

    Notes
    Added
    • Side effect annotation now allows automatic refresh after a custom action caused changes
    Changed
    • Added a check to disable change tracking for views with a UNION
    Fixed
    • Handling of associations within change tracked entities
    • Handling of change log when custom actions on child entities are called
  19. v1.0.3

    Features 2, Changed 2

    Notes
    Added
    • Added note about using SAPUI5 v1.120.0 or later for proper lazy loading of the Change History table.
    • In README, add warning about tracking personal data.
    Changed
    • Support cases where parent/child entries are created simultaneously.
    • Allow for lazy loading of change history table (with SAP UI5 release 1.120.0).
  20. v1.0.2

    Changed 1

    Notes
    Changed
    • In README, use view of the full change-tracking table instead of the customized one for the main image.
  21. v1.0.1

    Changed 1, Fixes 1

    Notes
    Changed
    • Flattened README structure.
    Fixed
    • Labels are looked up from the service entity (not the db entity only).
  22. v1.0.0

    Features 1

    Notes
    Added
    • Initial release
9,484downloads per week, npm
Weekly downloads for @cap-js/change-tracking26 complete calendar weeks from 2026-04-20 to 2026-10-18; the latest total is 9484.20 Apr – 26 Apr: 7,68327 Apr – 3 May: 6,7334 May – 10 May: 7,12211 May – 17 May: 6,28918 May – 24 May: 6,62825 May – 31 May: 6,3751 Jun – 7 Jun: 5,9178 Jun – 14 Jun: 7,23715 Jun – 21 Jun: 8,31822 Jun – 28 Jun: 7,78329 Jun – 5 Jul: 7,9396 Jul – 12 Jul: 8,00713 Jul – 19 Jul: 8,35720 Jul – 26 Jul: 8,54027 Jul – 2 Aug: 9,5733 Aug – 9 Aug: 10,09210 Aug – 16 Aug: 8,92717 Aug – 23 Aug: 11,40724 Aug – 30 Aug: 9,70731 Aug – 6 Sept: 9,4647 Sept – 13 Sept: 8,45614 Sept – 20 Sept: 13,77021 Sept – 27 Sept: 16,01628 Sept – 4 Oct: 19,0485 Oct – 11 Oct: 14,51812 Oct – 18 Oct: 9,484
Change tracking
  1. com.sap.cds:cds-feature-change-trackingChange log of annotated entity fields for CAP Javafrom CAP
  2. cdsmbt01CDS plugin for OData change tracking in CAP applicationsfrom CAP

Details

Sources5
npm registrydiscovered herechecked
GitHub APIdiscovered here, cap-jschecked
npm registry, @cap-js/change-trackingversion, releases, installschecked
github.com/git+https://github.com/cap-js/change-tracking.gittree, pushes, contributors, CIchecked
README.mdquoted, and what the entry declareschecked

Sources are graph entities: the same page may back several entries, and an unreachable source flags every row that rests on it.

Compatibility and provenance
runtimesnode
declared for>=8.5
latestv2.2.3
first release
releases and cadence39, ~8d median
discovered vianpm registry, GitHub API · cap-js
first seen in the atlas
ecosystem and domaincap, operations-and-runtime
Contributors22
History in the atlas1
  1. First seen.
For your agent
Ref
artifact:cap-js-change-tracking
JSON
/artifacts/cap-js-change-tracking.json
MCP
Connecting an agent