SuperAPI AI Directory

multitenancy

General & other

View repository

Updated 10-16 02:37

About this skill

Multitenancy — CAP Best Practices

Primary reference: https://cap.cloud.sap/docs/guides/multitenancy/ MTX Services: https://cap.cloud.sap/docs/guides/multitenancy/mtxs

Setup with @sap/cds-mtxs (built-in MTX, recommended)

npm install @sap/cds-mtxs

CDS 10: @sap/cds-mtxs is now v4.x. The old @sap/cds-mtx package and the cds migrate migration command have been removed — if you haven't migrated yet, use an older @sap/cds-mtxs v3.x to perform the migration first, then upgrade to v4.

npm install @sap/cds-mtxs  # installs v4.x with CDS 10

package.json:

{
  "cds": {
    "profile": "with-mtx-sidecar",
    "requires": {
      "multitenancy": true,
      "extensibility": true,
      "toggles": true,
      "db": {
        "kind": "hana-mt"
      },
      "auth": {
        "kind": "xsuaa"
      }
    }
  }
}

xs-security.json for multitenancy

{
  "xsappname": "my-cap-app",
  "tenant-mode": "shared",
  "scopes": [
    { "name": "$XSAPPNAME.mtcallback", "grant-as-authority-to-apps": ["$XSAPPNAME!t*"] },
    { "name": "$XSAPPNAME.mtdeployment", "description": "Scope to deploy to tenants" }
  ],
  "authorities": ["$ACCEPT_GRANTED_AUTHORITIES"]
}

MTX Sidecar (for larger applications)

Add a separate sidecar module in mta.yaml:

- name: my-cap-app-mtx
  type: nodejs
  path: mtx/sidecar
  build-parameters:
    builder: npm
    build-result: .
  parameters:
    memory: 512M
  requires:
    - name: my-cap-app-uaa
    - name: my-cap-app-registry
    - name: my-cap-app-db      # service manager, not hdi-container!

mtx/sidecar/package.json:

{
  "name": "my-cap-app-mtx",
  "dependencies": { "@sap/cds": "*", "@sap/cds-mtxs": "*" },
  "cds": {
    "profile": "mtx-sidecar"
  }
}

Tenant lifecycle hooks

// srv/provisioning-service.js
const cds = require('@sap/cds')

cds.on('mtx.tenant.provisioned', async ({ tenant }) => {
  console.log(`Tenant ${tenant} has been onboarded`)
  // Seed initial data, set up defaults, send welcome email, etc.
})

cds.on('mtx.tenant.deprovisioned', async ({ tenant }) => {
  console.log(`Tenant ${tenant} has been offboarded`)
  // Archive data, notify admins
})

cds.on('mtx.tenant.upgraded', async ({ tenant }) => {
  console.log(`Tenant ${tenant} schema upgraded`)
})

Tenant-specific configuration

// In a handler, always scope to the current tenant
async onReadConfig(req) {
  const { tenant } = req
  const config = await SELECT.one(TenantConfig).where({ tenant_ID: tenant })
  return config
}

Never use a single global config — always filter by req.tenant.

Extensibility: tenant-specific CDS extensions

Tenants can extend the base model (if extensibility: true):

// Tenant extension (uploaded via MTX API)
extend entity Products with {
  tenantField : String(100);
}

Deploy tenant extension:

cds push --to https://my-app-mtx.cfapps.eu10.hana.ondemand.com \
         --user alice@tenant.example.com

Service Manager (replaces HDI containers per tenant)

- name: my-cap-app-db
  type: org.cloudfoundry.managed-service
  parameters:
    service: service-manager
    service-plan: container

CAP's hana-mt database kind auto-provisions one HDI container per tenant via Service Manager.

Local multitenancy testing

{
  "cds": {
    "requires": {
      "multitenancy": true,
      "db": { "kind": "sqlite" },
      "auth": {
        "kind": "mocked",
        "users": {
          "alice": { "tenant": "tenant-A", "roles": ["admin"] },
          "bob":   { "tenant": "tenant-B", "roles": ["viewer"] }
        }
      }
    }
  }
}

CAP creates separate in-memory SQLite DBs per tenant automatically.

Common mistakes to avoid

  • ❌ Using hdi-container instead of service-manager — hdi-container is single-tenant only
  • ❌ Storing shared (cross-tenant) data in a tenant HDI container
  • ❌ Hardcoding tenant-mode: dedicated in xs-security.json for a shared SaaS app
  • ❌ Forgetting $ACCEPT_GRANTED_AUTHORITIES in xs-security.json — breaks MTX callback
  • ❌ Not seeding initial data in the mtx.tenant.provisioned hook
  • ❌ Running schema upgrades manually — always use the MTX upgrade API