Primary reference: https://cap.cloud.sap/docs/releases/2026/jun26
Migration guide: https://cap.cloud.sap/docs/releases/migration/cds10
Package version requirements
// package.json — use matching major versions for all @cap-js packages
{
"engines": { "node": ">=22" },
"dependencies": {
"@sap/cds": "^10",
"@sap/cds-dk": "^10",
"@sap/cds-mtxs": "^4",
"@cap-js/sqlite": "^3",
"@cap-js/hana": "^3"
}
}
⚠️ Don't mix versions: Never use
@sap/cds10.x with@sap/cds-mtxs3.x or@cap-js/hana2.x — versions must align.
CAP Java 5 requires JDK 21 (minimum) or JDK 25 (recommended) and Spring Boot 4.1+.
Use cds upgrade to check your project
npm install -g @sap/cds-dk
cds upgrade
The cds upgrade tool (Alpha in CDS 10) scans your project for breaking changes and provides migration guidance for each finding.
Step-by-step migration checklist
Step 1: Meet Node.js requirements
| Node.js version | Status in CDS 10 |
|---|---|
| Node.js v20 | Not supported (EOL April 2026) |
| Node.js v22 | Minimum required |
| Node.js v24 | Recommended (Active LTS) |
| Node.js v26 | Already tested, usable in dev |
node --version # must be >= 22
Step 2: Upgrade packages
npm install @sap/cds@latest @sap/cds-dk@latest
npm install @cap-js/sqlite@latest @cap-js/hana@latest
npm install @sap/cds-mtxs@latest # if you use multitenancy
Step 3: Fix CDS compiler errors — improved checks
The CDS compiler now reports security annotation mismatches as errors (not warnings):
// ❌ Typo in artifact name — was a warning, now an error
annotate AdmnService with @requires: 'admin'; // "AdmnService" doesn't exist
// ❌ Duplicate element extension — now an error
extend MyEntity with { field: String; };
extend MyEntity with { field: Date; }; // duplicate!
// ❌ Invalid default on structured element — now an error
type Address { street: String; city: String; }
entity Foo { addr: Address default 'Berlin'; }
Run to find all errors:
cds compile '*'
Step 4: Handle breaking behavior changes
A. Bypass Drafts by Default (bypass_draft: true)
Impact: Direct access to active entities now skips draft choreography by default.
// CDS 10: These access ACTIVE entity directly (no draft processing)
GET /odata/v4/Orders(123) // ✓ reads active directly
PATCH /odata/v4/Orders(123) // ✓ updates active directly
DELETE /odata/v4/Orders(123) // ✓ deletes active directly
If you need the old behavior (always go through draft choreography):
// in srv/cat-service.js
cds.fiori.bypass_draft = false
Handler impact: Validation handlers must now handle both draft AND active entity paths:
this.on('PATCH', Orders, req => {
// Called for: PATCH /draft/Orders(id) AND PATCH /Orders(id) in CDS 10
// (only for draft path before CDS 10)
})
B. Consolidated Write Results
INSERT now returns an iterable array with generated keys:
const result = await INSERT.into(Products).entries(
{ title: 'Book 1', price: 29.99 },
{ title: 'Book 2', price: 19.99 }
)
const [book1, book2] = [...result] // generated IDs
UPDATE/DELETE return { affected: number }:
const { affected } = await UPDATE(Products, id).with({ price: 24.99 })
const { affected: deleted } = await DELETE.from(Orders).where(...)
Handler note: Access input via req.data in handlers — don't rely on the return value.
C. IEEE754Compatible enabled by default
Decimal and Int64 fields now return as strings (not numbers):
// CDS 9: { amount: 123.45 } — loss of precision for large decimals
// CDS 10: { amount: "123.45" } — full precision preserved, type is string
In TypeScript/client code, parse as needed:
const amount = parseFloat(order.amount) // string → number
const price = new Decimal(order.price) // string → Decimal
Disable if needed (⚠️ not recommended):
// srv/cat-service.js
cds.env.build.ieee754compatible = false
D. srv.entities — now a getter, not a callable
// CDS 9: srv.entities was a method
const { Books } = srv.entities() // ❌ error in CDS 10
// CDS 10: srv.entities is a getter
const { Books } = srv.entities // ✓ correct
E. Removed compatibility flags
These flags have been removed in CDS 10:
| Flag | Alternative |
|---|---|
cds.compat.consistent_params |
Already built-in |
cds.compat.compat_save_drafts |
Use cds.fiori.bypass_draft: false instead |
cds.compat.assert_not_null |
Stricter validation now default |
CAP Java migration (Spring Boot 4.1+)
| Requirement | CDS 9 | CDS 10 |
|---|---|---|
| JDK | 17, 21 | 21 minimum, 25 recommended |
| Spring Boot | 3.2, 4.0 | 4.1 minimum |
| Spring Security | 6.x | 7.0+ |
| Maven | 3.8.8+ | 3.9.14+ |
Automatic migration with OpenRewrite
CAP Java 5 provides OpenRewrite recipes that automate the bulk of required code changes:
mvn org.openrewrite.maven:rewrite-maven-plugin:run \
-Drewrite.recipeArtifactCoordinates=com.sap.cds:cds-services-recipes:5.0.0 \
-Drewrite.activeRecipes=com.sap.cds.services.migrations.Cap_5.0
This handles most deprecated API replacements and property migrations automatically.
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<spring-boot.version>4.1.0</spring-boot.version>
</properties>
<dependencies>
<!-- CDS 10 -->
<dependency>
<groupId>com.sap.cds</groupId>
<artifactId>cds-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!-- Spring Boot 4.1 with Spring Security 7.0 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
Going Native — third-party dependencies removed
In CDS 10 these replacements are defaults (no longer opt-in feature flags):
| Removed dependency | Replaced by |
|---|---|
axios |
Node-native Fetch API |
better-sqlite3 |
Node-native node:sqlite |
generic-pool |
Built-in connection pool |
@sap-cloud-sdk/http-client |
Native Fetch (in dev; Cloud SDK still needed for production OAuth) |
Total footprint reduction: ~112 packages, ~23 MB. No code changes needed.
In production or hybrid scenarios that need BTP Destination OAuth:
@sap-cloud-sdk/http-clientis still required — add it explicitly withnpm add @sap-cloud-sdk/http-client.
Vitest migration (Jest deprecated)
CDS 10 recommendation: Use Vitest instead of Jest:
npm uninstall jest
npm install --save-dev vitest chai
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
export default defineConfig({
test: {
globals: true,
testEnvironment: 'node',
coverage: {
provider: 'v8'
}
}
})
Why: Chai v6 (used in CAP tests) has full support only with Vitest; Jest has partial compatibility.
Health check — verify your migration
# 1. Compile without errors
cds compile '*'
# 2. All tests pass
npm test
# 3. Handlers use class-based pattern with super.init()
grep -r "class.*ApplicationService" srv/
grep -r "return super.init()" srv/
# 4. No old bypass_draft flag references
grep -r "bypass_draft" . --include="*.js"
# 5. No remaining Jest imports (should be Vitest)
grep -r "from 'jest'" test/
# 6. No srv.entities() calls (should be srv.entities)
grep -r "srv.entities()" srv/
Quick migration path
- Node.js: Upgrade to v22+
- Packages:
npm install @sap/cds@latest - Compile:
cds compile '*'— fix all errors - Tests: Update Jest → Vitest (if applicable)
- Handlers: Verify
return super.init()in init() - Results: Handle new write result shapes (INSERT iterable, UPDATE/DELETE
{ affected }) - Drafts: Test if
bypass_draft: truedefault works or needsfalse - CAP Java: Upgrade JDK to 21, Spring Boot to 4.1
- Run tests:
npm test - Deploy: Follow your deployment pipeline
Common mistakes to avoid
- ❌ Mixing @sap/cds 10.x with @cap-js/sqlite 2.x or @sap/cds-mtxs 3.x
- ❌ Forgetting Node.js v22 requirement (v20 is EOL)
- ❌ Not handling new write result shapes (INSERT iterable, UPDATE/DELETE
{ affected }) - ❌ Calling
srv.entities()instead of usingsrv.entitiesas getter - ❌ Not updating validation handlers for
bypass_draft: truedefault - ❌ Continuing with Jest instead of migrating to Vitest
- ❌ Using old CAP Java (JDK 17) — must be 21+