Skip to main content

sap-cap-advanced

Advanced SAP CAP skill for enterprise patterns. Use when building multi-tenant CAP apps, deploying to Kyma, implementing XSUAA/IAS auth, advanced CDS modeling, or CAP remote services. If the user mentions CAP multitenancy, CAP Kyma deploy, mtxs, CAP extensibility, or advanced CDS aspects/compositions, use this skill.

Jump to install

Source facts

Repository
efeumutaslan/SAP-SKILLS
Last source activity
March 24, 2026 at 21:17
Detected SKILL.md language
English
Stars
5
Forks
1

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
4 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
sap-cap-advanced
description
Advanced SAP CAP skill for enterprise patterns. Use when building multi-tenant CAP apps, deploying to Kyma, implementing XSUAA/IAS auth, advanced CDS modeling, or CAP remote services. If the user mentions CAP multitenancy, CAP Kyma deploy, mtxs, CAP extensibility, or advanced CDS aspects/compositions, use this skill.
license
MIT
metadata
{"author":"SAP Skills Community","version":"1.0.0","last_verified":"2026-03-24"}
# Advanced CAP — Multitenancy, Kyma & Enterprise Patterns ## Related Skills - `sap-hana-cloud` — HANA Cloud as CAP persistence layer - `sap-kyma-runtime` — Deploying CAP on Kyma/Kubernetes - `sap-security-authorization` — XSUAA/IAS integration with CAP - `sap-event-mesh` — CAP messaging with Event Mesh ## Quick Start **Advanced CAP project setup:** ```bash # Create CAP project with advanced features cds init my-project --add hana,xsuaa,multitenancy,approuter,helm # Add individual features cds add hana # HANA Cloud persistence cds add xsuaa # Authentication cds add multitenancy # SaaS multitenancy cds add extensibility # Tenant extensions cds add helm # Kyma/K8s deployment cds add mtx # MTX sidecar for tenant management cds add messaging # Event Mesh integration cds add toggles # Feature toggles ``` ## Core Concepts ### CAP Multitenancy Architecture ``` ┌─────────────────────────┐ │ SaaS Application │ │ (CAP + MTX Sidecar) │ └──────────┬──────────────┘ │ ┌────────────────┼────────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ HDI Tenant A │ │ HDI Tenant B │ │ HDI Tenant C │ │ (Schema A) │ │ (Schema B) │ │ (Schema C) │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ └────────────────┼─────────────────┘ ▼ ┌──────────────────┐ │ HANA Cloud DB │ └──────────────────┘ ``` ### CAP Deployment Targets | Target | Command | Artifacts | |--------|---------|-----------| | Cloud Foundry | `cds build --for cf` | `mta.yaml` → `cf deploy` | | Kyma/K8s | `cds build --for kyma` | Helm chart → `helm upgrade` | | Hybrid (local+cloud DB) | `cds bind` + `cds watch` | Local app + remote HANA | ### CDS Advanced Modeling Features | Feature | Use Case | |---------|----------| | **Aspects** | Reusable field sets (managed, temporal, cuid) | | **Compositions** | Document structures (header→items) | | **Associations** | Cross-entity navigation | | **Projections** | Service-level views (rename, exclude, redirect) | | **Temporal data** | Bitemporal tables (valid-from/to, system time) | | **Localized data** | Multi-language texts (auto-generated `_texts` table) | | **Extensibility** | Tenant-specific model extensions | ## Common Patterns ### Pattern 1: Multitenancy Setup ```json // package.json — MTX configuration { "cds": { "requires": { "multitenancy": true, "extensibility": true, "toggles": true, "[production]": { "db": { "kind": "hana", "deploy-format": "hdbtable" }, "auth": { "kind": "xsuaa" } } }, "mtx": { "element-prefix": ["Z_", "z_"], "namespace-blocklist": ["com.sap.", "sap."], "extension-allowlist": [ { "for": ["my.bookshop.Books"], "new-fields": 5 }, { "for": ["my.bookshop.Authors"], "new-fields": 3 } ] } } } ``` ```javascript // srv/tenant-provisioning.js — Custom tenant lifecycle const cds = require('@sap/cds'); module.exports = class TenantProvisioning extends cds.ApplicationService { async init() { this.on('CREATE', 'tenant', async (req, next) => { const { subscribedTenantId, subscribedSubdomain } = req.data; console.log(`Provisioning tenant: ${subscribedTenantId}`); // Standard HDI container creation await next(); // Custom post-provisioning (seed data, config) await this.seedTenantData(subscribedTenantId); }); this.on('DELETE', 'tenant', async (req, next) => { const { subscribedTenantId } = req.data; console.log(`Deprovisioning tenant: ${subscribedTenantId}`); // Custom cleanup before HDI container deletion await this.archiveTenantData(subscribedTenantId); await next(); }); await super.init(); } async seedTenantData(tenantId) { const db = await cds.connect.to('db'); const { Configurations } = db.entities('my.app'); await db.run(INSERT.into(Configurations).entries([ { key: 'CURRENCY', value: 'EUR' }, { key: 'LANGUAGE', value: 'EN' } ])); } }; ``` ### Pattern 2: CAP + Kyma Helm Deployment ```yaml # chart/values.yaml global: domain: my-cluster.kyma.ondemand.com imagePullSecret: name: docker-registry-secret srv: image: repository: my-registry.io/my-app-srv tag: latest bindings: db: serviceInstanceName: hana-hdi auth: serviceInstanceName: xsuaa-instance resources: limits: memory: 512Mi cpu: 500m requests: memory: 256Mi cpu: 100m hana-deployer: image: repository: my-registry.io/my-app-hana-deployer tag: latest bindings: hana: serviceInstanceName: hana-hdi html5-apps-deployer: image: repository: my-registry.io/my-app-html5-deployer tag: latest ``` ```bash # Build and deploy to Kyma cds build --for kyma docker build -t my-registry.io/my-app-srv:latest -f gen/srv/Dockerfile . docker push my-registry.io/my-app-srv:latest helm upgrade --install my-app ./chart \ --namespace my-namespace \ --set-file xsuaa.jsonParameters=xs-security.json ``` ### Pattern 3: Remote Service Integration (Mashup) ```cds // srv/external/API_BUSINESS_PARTNER.cds (imported) // cds import API_BUSINESS_PARTNER --from <edmx-url> using { API_BUSINESS_PARTNER as bupa } from './external/API_BUSINESS_PARTNER'; // srv/mashup-service.cds — Combine local + remote service MashupService { // Local entity entity Orders as projection on my.Orders { *, customer: redirected to Customers }; // Remote entity (federated from S/4HANA) @readonly entity Customers as projection on bupa.A_BusinessPartner { key BusinessPartner as ID, BusinessPartnerFullName as name, to_BusinessPartnerAddress as addresses }; } ``` ```javascript // srv/mashup-service.js — Remote service handler const cds = require('@sap/cds'); module.exports = class MashupService extends cds.ApplicationService { async init() { const bupa = await cds.connect.to('API_BUSINESS_PARTNER'); // Delegate reads to remote service this.on('READ', 'Customers', (req) => bupa.run(req.query)); // Enrich local orders with remote customer data this.after('READ', 'Orders', async (orders) => { const customerIds = [...new Set(orders.map(o => o.customer_ID))]; if (customerIds.length === 0) return; const customers = await bupa.run( SELECT.from('A_BusinessPartner') .where({ BusinessPartner: { in: customerIds } }) .columns('BusinessPartner', 'BusinessPartnerFullName') ); const customerMap = new Map(customers.map(c => [c.BusinessPartner, c])); for (const order of orders) { order.customerName = customerMap.get(order.customer_ID)?.BusinessPartnerFullName; } }); await super.init(); } }; ``` ### Pattern 4: Advanced Authorization ```cds // srv/admin-service.cds @requires: 'authenticated-user' service AdminService { @restrict: [ { grant: 'READ', to: 'Viewer' }, { grant: 'WRITE', to: 'Editor' }, { grant: '*', to: 'Admin' } ] entity Products as projection on my.Products; // Instance-based auth (row-level) @restrict: [ { grant: 'READ', to: 'SalesRep', where: 'region = $user.region' }, { grant: '*', to: 'SalesManager' } ] entity SalesOrders as projection on my.SalesOrders; } ``` ```json // xs-security.json { "xsappname": "my-app", "tenant-mode": "shared", "scopes": [ { "name": "$XSAPPNAME.Viewer", "description": "Read access" }, { "name": "$XSAPPNAME.Editor", "description": "Write access" }, { "name": "$XSAPPNAME.Admin", "description": "Full access" } ], "role-templates": [ { "name": "Viewer", "scope-references": ["$XSAPPNAME.Viewer"], "attribute-references": [{ "name": "region" }] }, { "name": "Editor", "scope-references": ["$XSAPPNAME.Viewer", "$XSAPPNAME.Editor"], "attribute-references": [{ "name": "region" }] }, { "name": "Admin", "scope-references": ["$XSAPPNAME.Admin"] } ] } ``` ### Pattern 5: CAP with HANA Native Artifacts ```cds // db/schema.cds — Use HANA-specific features using { managed, cuid } from '@sap/cds/common'; @cds.persistence.journal // Enable temporal tables entity AuditLog : cuid, managed { action : String(50); entity : String(100); oldValue : LargeString; newValue : LargeString; } // Use HANA calculation view @cds.persistence.exists // Don't generate table, it exists in HANA entity SalesDashboard { key region : String(10); totalRevenue : Decimal(15,2); orderCount : Integer; avgOrderValue : Decimal(15,2); } ``` ```json // db/src/CV_SALES_DASHBOARD.hdbcalculationview // (Graphical calculation view created in BAS) // Referenced by @cds.persistence.exists entity above ``` ```sql -- db/src/SP_COMPLEX_CALC.hdbprocedure PROCEDURE "SP_COMPLEX_CALC" ( IN iv_year INTEGER, OUT et_result TABLE (region NVARCHAR(10), revenue DECIMAL(15,2)) ) LANGUAGE SQLSCRIPT SQL SECURITY INVOKER READS SQL DATA AS BEGIN et_result = SELECT region, SUM(amount) AS revenue FROM "MY_APP_ORDERS" WHERE YEAR("createdAt") = :iv_year GROUP BY region; END; ``` ### Pattern 6: CAP Feature Toggles ```javascript // srv/feature-service.js const cds = require('@sap/cds'); const toggles = require('@sap/cds-mtx/toggles'); module.exports = class FeatureService extends cds.ApplicationService { async init() { this.before('CREATE', 'Orders', async (req) => { // Check if new pricing engine is enabled for this tenant if (await toggles.isEnabled('new-pricing-engine', req.tenant)) { req.data.price = await this.calculateNewPrice(req.data); } else { req.data.price = await this.calculateLegacyPrice(req.data); } }); await super.init(); } }; ``` ## Error Catalog | Error | Message | Root Cause | Fix | |-------|---------|------------|-----| | `TENANT_NOT_FOUND` | No HDI container for tenant | Tenant not provisioned | Run tenant subscription; check MTX sidecar logs | | `CDS_COMPILE_ERROR` | Model compilation failed | Invalid CDS syntax or circular dependency | Run `cds compile --to sql` locally to debug | | `XSUAA: Forbidden` | JWT scope mismatch | Missing role assignment | Assign role collection to user in BTP cockpit | | `Remote service: 401` | Destination auth failed | Expired OAuth token or wrong destination config | Check BTP destination; regenerate credentials | | `HDI deploy: 409` | Container already exists | Duplicate tenant provisioning | Check MTX sidecar idempotency; clean up orphans | | Helm: `ImagePullBackOff` | Cannot pull container image | Wrong registry or missing secret | Verify image exists; check imagePullSecret | | `cds bind: Service not found` | Binding resolution failed | Service key missing or wrong name | Run `cf service-key <instance> <key>` to verify | | `HANA: Column store error` | Schema deployment failed | Incompatible schema evolution | Check `.hdbmigrationtable` for migration steps | ## Performance Tips 1. **Batch remote calls** — Use `$batch` for multiple remote service requests; CAP auto-batches with `cds.ql` 2. **Projection pushdown** — Use `columns()` in queries; CAP pushes column selection to HANA 3. **Expand wisely** — `$expand` with deep nesting causes N+1; prefer flat queries with client-side assembly 4. **Connection pooling** — CAP pools HANA connections by default; tune `pool.max` for high-concurrency
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub