---
title: December 2025
versions:
  cdsjs: 9.6.0+
  cdsdk: 9.6.0+
  cdsc: 6.6.0+
  cdsmtxs: 3.6.0+
  java: 4.6.0+
---

# December 2025

<ReleaseBadges />

[[toc]]



## Status-Transition Flows <Gamma/>

The Status Flows feature was rolled out as <Beta /> [in November](nov25#status-transition-flows) and is now promoted to <Gamma /> level. This means it is finalized, ready to use, stable, and supported long term in the documented feature set.

```cds
annotate TravelService.Travels with @flow.status: Status actions {
  acceptTravel    @from: [ #Open ] @to: #Accepted;
  rejectTravel    @from: [ #Open ] @to: #Rejected;
  deductDiscount  @from: [ #Open ]; // restricted to #Open travels
}
```

As part of this the documentation moved to [*Cookbook > Status Flows*](/@external/guides/services/status-flows), got overhauled and cleaned up of implementation details which are not considered public.



## Declarative Constraints <Gamma/>

Use the new [`@assert`](/@external/guides/services/constraints#assert-constraint) annotation to express conditions using [CDS Expression Language (CXL)](/@external/cds/cxl). The system validates these conditions whenever you write data. For example, the following constraints are defined on the `TravelService.Travels` entity:

```cds
using { TravelService } from './travel-service';
annotate TravelService.Travels with {

  Description @assert: (case
    when length(Description) < 3 then 'Description too short'
  end);

  Customer @assert: (case
    when Customer is null then 'Customer must be specified'
    when not exists Customer then 'Customer does not exist'
  end);

  BeginDate @mandatory @assert: (case
    when BeginDate > EndDate then 'ASSERT_BEGINDATE_BEFORE_ENDDATE'
    when exists Bookings [Flight.date < Travel.BeginDate]
      then 'ASSERT_BOOKINGS_IN_TRAVEL_PERIOD'
  end);

}
```

[Learn more about **Declarative Constraints**](/@external/guides/services/constraints){.learn-more}




## New `@hierarchy` Annotation

Annotations for hierarchical tree views have been greatly streamlined. Simply annotate a respective entity with the new `@hierarchy` annotation like this:

```cds
annotate AdminService.Genres with @hierarchy;
```

Which replaces all of these elaborate OData annotations you had to add previously:

<img src="./assets/dec25/hierarchy.png" alt="Former annotations required" style="zoom:40%;"/>

The rest stays as before, and as documented in [*Serving Fiori UIs – Hierarchical Tree Views*](/@external/guides/uis/fiori#fiori-tree-views). Find in the guide also additional details and usage options about the new `@hierarchy` annotation.






## New `cds export` <Beta/>

Use `cds export` to create API client packages from CDS service definitions.
For example, given a service definition like that:

::: code-group
```cds [srv/data-service.cds]
service FlightsService {
  @readonly entity Flights as projection on my.Flights;
  @readonly entity Airlines as projection on my.Airlines;
  @readonly entity Airports as projection on my.Airports;
}
```
[See the full source in _@capire/xflights_](https://github.com/capire/xflights/tree/main/srv/data-service.cds){.learn-more}
:::

We can create an API client package using the following command:

```sh
cds export srv/data-service.cds
```

The output is a full-featured CAP reuse package, which can be published to *npm* registries:

```sh
npm publish ./apis/data-service
```

... and consumed by other CAP projects using standard `npm add` and `npm update`:

```sh
npm add @capire/xflights-data
```

In addition `cds export` applies CAP plugin techniques to the generated package, so consumers benefit from plug & play, without any further configuration required.

> [!tip]  Why use cds export?
> Instead of exporting APIs to OData EDMX and `cds import`ing them to consuming apps, `cds export` creates ready-to-use API client packages, with **lossless** CDS API models.


[Learn more about **cds export**.](/@external/tools/cds-cli#cds-export){.learn-more}




## CDS Language & Compiler {#cds}



### Loading from `app/*` subfolders

By default, `cds build` or `cds serve` now automatically fetch and load all `.cds` files not only from the `./app` folder itself but also from all `./app/*` subfolders in there. For example, if you have Fiori apps organized in subfolders like shown in the snapshot below, all the `.cds` files will be loaded automatically.

<img src="./assets/dec25/app-star.png" alt="folders with .cds files" style="zoom:50%;" />

Thereby we eliminate the need to add an `./app/index.cds` with `using` directives for each file in subfolders like that:

```cds
// Import all .cds files from common folder
using from './common/code-lists';
using from './common/common';
using from './common/labels';

// Import all .cds files from travels folder
using from './travels/capabilities';
using from './travels/field-control';
using from './travels/layouts';
```

You can disable this by setting <Config>cds.folders.apps: false</Config>.
You can apply the same for other folders by e.g. <Config>cds.folders.srvs: srv/*</Config>.



### Enums in Annotation Expressions

Up to now, you could use enum symbols in annotation expressions only
for enums defined in CDS. As value for an OData annotation, you had to provide the
actual value and then explain the meaning of these funny numbers. Now, you can directly use the enum symbols:

<!-- cds-mode: ignore -->
```cds
entity Travel {
  key TravelUUID : UUID;
  status : String(1) enum { Open = 'O'; Accepted = 'A'; Canceled = 'X'; };

  // 1 is ReadOnly, 7 is Mandatory                    // [!code --]
  @Common.FieldControl: (status = #Accepted ? 1 : 7)  // [!code --]
  @Common.FieldControl: (status = #Accepted ? #ReadOnly : #Mandatory)  // [!code ++]
  bookingFee : Decimal(16,3) default 0;
}
```

[Learn more about **Annotation Expressions**.](/@external/cds/cdl#expressions-as-annotation-values){.learn-more}


## Node.js {#cds-js}


### Direct CRUD on Draft-enabled Entities <Beta />

With <Config>cds.fiori.direct_crud:true</Config>, the Node.js runtime now allows direct CRUD requests to draft-enabled entities.
For example, instead of having to follow the draft request sequence of `EDIT`, `PATCH`, and `SAVE`, you can send direct `UPDATE` requests to the so-called _active entity_.

[Learn more about draft-specific events.](/@external/node.js/fiori#draft-specific-events){.learn-more}

In such direct requests, the additional key `IsActiveEntity` defaults to `true`, which allows you to omit it:
```http
POST {{server}}/odata/v4/travel/Travels
{ "ID": 4711 }
```
```http
PUT {{server}}/odata/v4/travel/Travels(ID=4711)
{ "Description": "Fun times!" }
```

::: tip No code changes required
Note that with this feature, the `POST` request creates the active entity.
Hence, even though the feature works seamlessly with existing implementations and also SAP Fiori elements adjusts on the fly, it may affect your tests.
:::

[Learn more about direct CRUD on draft-enabled entities.](/@external/guides/uis/fiori#requests-to-active-data){.learn-more}




### Improved Event Queue Processing

We consolidated event queue processing into a default queue and custom queues (if configured).

This change is transparent for projects except for minor content changes in the technical data structure (`cds.outbox.Messages`) that _may_ show up in custom-built extensions for [managing the Dead Letter Queue](/@external/guides/events/event-queues#dead-letter-queue).

Further, we increased the db-related guarantee from _at least once_ to _exactly once_.
That is, the system only commits database changes from event processing if it successfully processes the event, and vice-versa.

[Learn more about Event Queues in Node.js](/@external/node.js/event-queues){.learn-more}


### Cleaned-up Model Reflection APIs

The following function usages are officially deprecated and will be removed in the next major version 10.

| Deprecated Usage | Official Usage                                                         |
|------------------|------------------------------------------------------------------------|
| `srv.entities()` | [`srv.entities`/ `cds.entities()`](/@external/node.js/cds-reflect#cds-service) |
| `srv.types()`    | [`srv.types`](/@external/node.js/cds-reflect#cds-service)                      |
| `srv.events()`   | [`srv.events`](/@external/node.js/cds-reflect#cds-service)                     |
| `srv.actions()`  | [`srv.actions`](/@external/node.js/cds-reflect#cds-service)                    |

Further from version 10 onwards, the `.texts` entities will not be included in the `srv.entities` variants anymore. Use the [`texts`](/@external/node.js/cds-reflect#-texts) property of the respective entity instead.

| Deprecated Usage                | Official Usage               |
|---------------------------------|------------------------------|
| `CatalogService['Books.texts']` | `CatalogService.Books.texts` |

You can enforce that behavior already today with <Config>cds.features.compat_texts_entities:false</Config>.

## Java {#cds-java}

### Important Changes ❗️ { .important #important-changes-in-java}

<div id="java-internal-important" />

### Deep Updates for Draft

So far, for draft-enabled entities, [deep updates](/@external/java/working-with-cql/query-api#deep-update) were only supported for the _active_ entities. Now, you may also execute deep updates on the _inactive_ entities provided that the update statement includes the keys for entities to be updated. [Searched updates](/@external/java/working-with-cql/query-api#searched-update) will be rejected.

The following example shows the recommended way to do this:

```java
DraftService draftService = ...
Orders entry = Orders.create();
entry.setId(123);
entry.setItems(...); // Some info about items

CqnUpdate deepUpdate =
  Update.entity(ORDERS, o -> o.filter(f -> f.IsActiveEntity().eq(false)))
        .entry(entry);
draftService.run(deepUpdate);
```

### Miscellaneous

#### API for Aliasing CqnValue

The `CqnValue` interface received a new `as(String alias)` method to create aliased values directly.
This makes it easier to build queries with aliased values, such as predicate expressions on the select list:

```java
CqnExpression xpr = cdsModel.getEntity(BOOKS.CDS_NAME).getAnnotationValue("@xpr", CQL.FALSE);

Select.from(BOOKS).columns(xpr.as("annotatedXpr")); // [!code highlight]
```

## Tools { #tools}

### `cds up` Convenience for Kyma

If your project contains Helm deployment resources and no _mta.yaml_ `cds up` will default to Kubernetes and you do not need to specify the `--to k8s` option anymore.

```sh
cds up --to k8s # [!code --]
cds up # [!code ++]
```

[Learn more about Kyma Deployment.](/@external/guides/deploy/to-kyma){.learn-more}

## Plugins

### @sap/cds-rfc

The Node.js plugin `@sap/cds-rfc` now supports multitenancy.

[Learn more about the ABAP RFC plugin.](/@external/plugins/index#abap-rfc){.learn-more}