Skip to content
Search

    MTX Services Reference

    Introduction & Overview

    The @sap/cds-mtxs package provides a set of CAP services which implement multitenancy, features toggles and extensibility (‘MTX’ stands for these three functionalities). These services work in concert as depicted in the following diagram.

    mtx-overview.drawio

    These services are implemented in Node.js and run in the same Node.js server as your application services, as well as in separate micro services called sidecars. In case of Java, those services always run in sidecars. All services can be consumed via REST APIs.

    As all services are defined and implemented as standard CAP services, with service definitions in CDS and implementations based on CAP Node.js framework, application projects can hook into all events to add custom logic using CAP Node.js.

    Getting Started…

    Add @sap/cds-mtxs Package Dependency

    npm add @sap/cds-mtxs
    

    Enable MTX Functionality

    Add one or more of the following convenience configuration flags, for example, to your package.json in a Node.js-based project:

      "cds": {
        "requires": {
          "multitenancy": true,
          "extensibility": true,
          "toggles": true,
        }
      }
    

    Learn more about MTX configuration. Java-based projects require a sidecar setup.

    Test-Drive Locally

    After having enabled MTX features as above, you can test MTX functionality with local development setups and in-memory databases as usual, for example, using cds watch to start your server:

    cds watch
    

    This produces an output like that, showing the MTX services being served in addition to your application services:

    image-20220704173230030

    Grow As You Go…

    Follow CAP principles of ‘Grow as you go…’ to minimize complexity of setups, stay in inner loops with fast turnarounds, and hence minimize costs and accelerate development.

    Develop without MTX

    During development you rarely need to run your servers with MTX functionality enabled. Only do so when your really need it. For example in certain tests and/or by using configuration profiles.

    Enable MTX only if required

    For example, this configuration would have development not using MTX by default, but you could occassionally run with MTX, as well as have it enabled always in production:

      "cds": {
        "requires": {
          "[with-mtx]": {
             "multitenancy": true,
             "extensibility": true,
             "toggles": true,
          },
          "[production]": {
             "multitenancy": true,
             "extensibility": true,
             "toggles": true,
          }
        }
      }
    

    During development you could occassionally run with MTX:

    cds watch --profile with-mtx
    

    Testing with minimal setup

    When designing your test suites, which run frequently in CI/CD pipelines, reduce runtimes and costs by having a set of functional tests run first, which use MTX in minimized setups, that is, with local servers and in-memory databases as introduced in the Getting Started chapter.

    Only in second and third phases run hybrid tests which, for example, test tenant subscriptions with SAP HANA, or full integration test, whith all cloud services.

    Consumption

    Via Programatic APIs

    Consume the MTX services using standard Service APIs. For example you could do this in cds repl:

    await cds.test()
    var { 'cds.xt.ModelProviderService': mps } = cds.services
    var { 'cds.xt.DeploymentService': ds } = cds.services
    var db = await ds.subscribe ('t1')
    var csn = await mps.getCsn('t1')
    cds.context = { tenant:'t1' }
    await db.run('SELECT type, name from sqlite_master')
    

    Via REST APIs

    Common usage of the MTX services is through REST APIs. Here’s an example:

    1. Start the server

      cds watch
      
    2. Subscribe a tenant

      POST  http://localhost:4004/-/cds/deployment/subscribe HTTP/1.1
      Content-Type: application/json
      
      {
        "tenant": "t1"
      }
      
    3. Get CSN from ModelProviderService

      POST http://localhost:4004/-/cds/model-provider/getCsn HTTP/1.1
      Content-Type: application/json
      Authorization: Basic yves:
         
      {
        "tenant": "t1",
        "toggles": ["*"]
      }
      

    Configuration

    Shortcuts cds.requires.multitenancy / extensibility / toggles

    The easiest way to enable multitenancy, extensibility, and feature toggles is as follows:

    {
      "cds": {
        "requires": {
          "multitenancy": true,
          "extensibility": true,
          "toggles": true,
        }
      }
    }
    

    On one hand, these settings are checked by CAP runtimes to support respective features, for examples, database service implementations use tenant-specific connection pools when multitenancy is switched on.

    On the other hand during server bootstrapping, these flags are checked to ensure the requisite combinations of services are served by default as follows:

      multitenancy extensibility toggles
    SaasProvisioningService yes    
    DeploymentService yes    
    ExtensibilityService   yes  
    ModelProviderService yes yes yes

    Configuring Individual Services

    In addition or alternatively to the convenient shortcuts above you can configure each service individually, as shown in the following examples.

      "cds": {
        "requires": {
          "cds.xt.DeploymentService": true
        }
      }
    

    The names of the service-individual configuration options are:

    • cds/requires/<service definition name>
    Allowed values are:
    • false — de-activates the service selectively
    • true — activates the service with defaults for embedded usage
    • <preset name> — uses preset, for example, with defaults for sidecar usage
    • { ...options } — add/override individual configuration options
    Common Config Options
    • model — specifies/overrides the service model to be used
    • impl — specifies/overrides the service implementation to be used
    • kind — the kind of service/consumption, for example, rest for remote usage

    These options are supported by all services.

    Combined with Convenience Flags

      "cds": {
        "requires": {
          "multitenancy": true,
          "cds.xt.SaasProvisioningService": false,
          "cds.xt.DeploymentService": false,
          "cds.xt.ModelProviderService": { "kind": "rest" }
        }
      }
    

    This tells the CAP runtime to enable multitenancy, but neither serve the DeploymentService, nor the SaasProvisioningService, and to use a remote ModelProviderService via REST protocol.

    Individual Configurations Only

    We can also use only the individual service configurations:

      "cds": {
        "requires": {
          "cds.xt.DeploymentService": true,
          "cds.xt.ModelProviderService": { "root": "../.." }
        }
      }
    

    In this case, the server will not run in multitenancy mode. Also extensibility and feature toggles are not supported. Yet the DeploymentService and the ModelProviderService are served selectively. For example, this kind of configuration is used in sidecars.

    Using Configuration Presets

    Some MTX services come with pre-defined configuration presets, which can easily be used by refering to the preset suffixes. For example, to simplify and standardize sidecar configuration, ModelProviderService supports the in-sidecar preset which can be used like that:

      "cds": {
        "requires": {
          "cds.xt.ModelProviderService": "in-sidecar"
        }
      }
    

    These presets are actually configured in cds.env defaults like that:

    cds: {
      requires: {
        // Configuration Presets (in cds.env.requires.kinds)
        kinds: {
          "cds.xt.ModelProviderService-in-sidecar": {
            "[development]": { root: "../.." },
            "[production]": { root: "_main" },
          },
          "cds.xt.ModelProviderService": {
            model: "@sap/cds/srv/model-provider"
          },
          // ...
        }
      }
    }
    

    Learn more about cds.env

    Inspecting Effective Configurations

    You can always inspect the effective configuration by executing cds env get requires within the mtx/sidecar folder, as shown in the screenshot below.

    image-20220630134932964

    Add CLI option --profile to inspect configurations as in different profiles:

    cds env get requires --profile development
    cds env get requires --profile production
    

    Customization

    All services are defined and implemented as standard CAP services, with service definitions in CDS, and implementations based on CAP Node.js framework. That means for you, you can easily do both, adapt service definitions, as well as hook into all events to add custom logic using CAP Node.js.

    Customizing Service Definitions

    For example, you could override the endpoints to serve a service:

    using { cds.xt.ModelProviderService } from '@sap/cds-mtxs';
    annotate ModelProviderService with @path: '/mtx/mps';
    

    Adding Custom Lifecycle Event Handlers

    For example in your server.js (or the one in your sidecar):

    const cds = require('@sap/cds')
    cds.on('served', ()=>{
      const { 'cds.xt.ModelProviderService': mps } = cds.services
      const { 'cds.xt.DeploymentService': ds } = cds.services
      ds.before ('upgrade', (req) => { ... })
      ds.after ('subscribe', (_,req) => { ... }
      mps.after ('getCsn', (csn) => { ... })
    })
    

    Sidecar Setups

    In the minimal setup introduced in the Getting Started… chapter, we had the MTX services being served embedded with our main app, that is, in the same server as our application services. While this is possible for Node.js and even recommended to reduce complexity during development, quite frequently, we’d want to run them in a separate micro service. Reasons for that include:

    • For Java-based projects — As these services are implemented in Node.js we need to run them separately and consume them remotely for Java-based apps.
    • To scale independently — As some operations, especially upgrade, are very resource-intensive, we want to scale these services separate from our main application.

    As these services are built and consumed as CAP services, we benefit from CAP’s agnostic design and can easily move them to separate services.

    Create Sidecar as a Node.js Subproject

    An MTX sidecar is a standard, yet minimalistic Node.js CAP project. By default it’s added to a subfolder mtx/sidecar within your main project, containing just a package.json file, as demonstrated in the following screenshot.

    image-20220630134350664

    1. Create a folder named mtx/sidecar.

    2. Add a package.json in there with content like that:

      {
        "name": "mtx-sidecar", "version": "0.0.0",
        "dependencies": {
          "@sap/cds-mtxs": "^1",
          "@sap/cds": "^6",
          "express": "^4"
        },
        "cds": {
          "requires": {
            "cds.xt.ModelProviderService": "in-sidecar",
            "cds.xt.SaasProvisioningService": true,
            "cds.xt.DeploymentService": true,
            "cds.xt.ExtensibilityService": true,
            "[development]": {
              "db": { "kind": "sqlite", "credentials": {
                "url": "../../sqlite.db"
              }}
            }
          },
          "[development]": {
            "requires": { "auth": "dummy" },
            "server": { "port": 4005 }
          }
        }
      }
      

    Let’s dissect the content above one by one…

    Package Dependencies

      ...
      "dependencies": {
        "@sap/cds-mtxs": "^1",
        "@sap/cds": "^6",
        "express": "^4"
      },
      ...
    

    This ensures we have the required software installed, in particular @sap/cds and @sap/cds-mtxs.

    Required MTX Services

      ...
      "cds": {
        "requires": {
          "cds.xt.ModelProviderService": "in-sidecar",
          "cds.xt.SaasProvisioningService": true,
          "cds.xt.DeploymentService": true,
          "cds.xt.ExtensibilityService": true,
          ...
    

    This selectively enables the services we want to be served in the sidecar using individual configuration. Here we enable all MTX services. You can choose to only serve some of which, according to your needs, of course.

    Important: ModelProviderService in sidecar always needs special configuration, as provided by the in-sidecar preset.

    Using Shared Database

          ...
          "[development]": {
            "db": { "kind": "sqlite", "credentials": {
              "url": "../../sqlite.db"
            }}
          }
          ...
    

    In case of multitenancy the DeploymentService needs to deploy the very database instances, which are subsequently used by the main application. This setting ensures that for local development with SQLite.

    Additional [development] Settings

        ...
        "[development]": {
          "requires": { "auth": "dummy" },
          "server": { "port": 4005 }
        }
        ...
    

    These additional settings for profile [development] are to support local tests with default values for the server port (different from the default port 4004 of the main app), and to allow any local calls to the sidecar (secured by default in production).

    Using Sidecars with Node.js

    As we are running the MTX services in the sidecar, we need to configure the main app to not run certain services, using some from sidecars and using shared databases.

    Not Serving Services Twice

    As we are running the MTX services in the sidecar we need to add configuration to the main app to overrule the default service provisioning involved with the convenience shortcuts like cds.requires.multitenancy:

      ...
      "cds": {
        "requires": {
          "multitenancy": "true",
          "cds.xt.DeploymentService": false,
          ...
        }
      }
      ...
    

    This setting disables both, DeploymentService as well as the SaasProvisioningService, as the latter depends on the former.

    Using Services from Sidecar

    In Node.js apps we usually don’t consume services from the sidecar. The ModelProviderService is usually served both, embedded in the main app as well as in the sidecar. The following is documented for the sake of completeness only…

    We can use the from-sidecar preset to tell the CAP runtime to use the remote model provider from the sidecar:

    "cds": {
      "requires": {
        "cds.xt.ModelProviderService": "from-sidecar"
      }
    }
    

    Using Shared Database

    In addition we have to ensure we use a shared database, which has to be a persistent one. So we override the default :memory: for SQLite as follows:

      ...
      "cds": {
        "requires": {
          ...
          "[development]": { "db": "sqlite" }
        }
      }
      ...
    

    Using Sidecars with Java

    Java applications need to run and maintain the MTX services in a sidecar application. How you can achieve this is described in the corresponding documentation.

    Testing Sidecar Setups

    With the above setup in place, we can test-drive the sidecar mode locally. To do so we’ll simply start the sidecar and the main app in separate shells.

    1. Run sidecar in first shell:

      cds w mtx/sidecar
      

      image-20220704184429972

    2. Run the main app as before in the second shell:

      cds w
      

    ModelProviderService serving models from main app

    When we use our application, we can see model-provider/getCsn requests in the sidecar’s trace log. In response to those requests, the sidecar reads and returns the main app’s models, that is, the models from two levels up the folder hierarchy as configured by the in-sidecar preset for development.

    Note: Service Bindings by cds watch

    Required service bindings are done automatically by cds watch’s built-in runtime service registry. This is how it works:

    1. Each server started using cds watch registers all served services in ~/cds-services.json.

    2. Every subsequently started server binds automatically all required remote services, to equally named services already registered in ~/cds-services.json.

    In our case: The main app’s ModelProviderService automatically receives the service binding credentials, for example url, to talk to the one served by the sidecar.

    Build Sidecar for Production

    When deploying a sidecar for production, it doesn’t have access to the main app’s models two levels up the deployed folder hierarchy. Instead we have to prepare deployment by running cds build in the project’s root:

    cds build
    

    One of the build tasks that are executed is the mtx-sidecar build task. It generates log output similar to the following:

    [fiori] cds build
    [cds] - determining build tasks for project ...
    [cds] - the following build tasks will be executed:
      {"for":"mtx-sidecar", "src":"mtx/sidecar", "options":... }
    [cds] - done > wrote output to:
       gen/mtx/sidecar/_main/fts/isbn/csn.json
       gen/mtx/sidecar/_main/fts/reviews/csn.json
       gen/mtx/sidecar/_main/resources.tgz
       gen/mtx/sidecar/_main/srv/_i18n/i18n.json
       gen/mtx/sidecar/_main/srv/csn.json
       gen/mtx/sidecar/package.json
       gen/mtx/sidecar/srv/_i18n/i18n.json
       gen/mtx/sidecar/srv/csn.json
    [cds] - build completed in 687 ms
    

    The outcome of that build task is a compiled and deployable version of the sidecar in the gen/mtx/sidecar staging areas, as shown in this screenshot:

    image-20220630150123330

    In essence, the mtx-sidecar build task does the following:

    1. It runs a standard Node.js build for the sidecar.
    2. It pre-compiles the main app’s models, including all features into respective csn.json files, packaged into ./_main subfolder.
    3. It collects all additional sources required for subsequent deployments to ./resources.tgz. For example, these include .csv and i18n files.

    Test-Drive Production Locally

    We can also test-drive the production-ready variant of the sidecar locally before actual deployment, again using two separate shells.

    1. First, start sidecar from gen/mtx/sidecar in prod simulation mode:

      cds w gen/mtx/sidecar --profile development,prod
      
    2. Second, start main app as usual:

      cds w
      

    ModelProviderService serving models from main app

    When we now use our application again, and inspect the sidecar’s trace logs, we see that the sidecar reads and returns the main app’s pre-compiled models from _main now:

    image-20220630151306758

    ModelProviderService

    The ModelProviderService serves model variants, which may include tenant-specific extensions and/or feature-toggled aspects.

       
    Service Definition @sap/cds-mtxs/srv/model-provider
    Service Definition Name cds.xt.ModelProviderService
    Default Http Endpoint /-/cds/model-provider

    Configuration

    ModelProviderService is activated and configured automatically whenever one of the shortcut settings cds.requires.multitenancy, .extensibility, or .toggles is activated as described in the Configuration.

    In addition, it can be activated — or deactivated — selectively by the like of:

        "cds": {
          "requires": {
            "cds.xt.ModelProviderService": true
          }
        }
    

    Supported values as documented in Configuring Individual Services above

    Individual Config Options
    • Common Config Options
    • root — a directory name, absolute or relative to the package.json’s location, specifying the location to search for models and resources to be served by the model provider services. Default is undefined, for embedded usage of model provider. In case of a sidecar, it refers to the main app’s model; usually "../.." during development, and "_main" in production.
    Supported Presets
    • in-sidecar — provides defaults for usage in sidecars
    • from-sidecar — shortcut for { "kind": "rest" }

    getCsn (tenant, toggles) → CSN

    Returns the application’s effective CSN document for the given tenant + feature toggles vector. CAP runtimes call that method to obtain the effective models to serve.

    Arguments  
    tenant a string identifying the tenant
    toggles an array listing toggled features; ['*'] for all features
    Example Usage
    POST http://localhost:4004/-/cds/model-provider/getCsn HTTP/1.1
    Content-Type: application/json
    Authorization: Basic yves:
    
    {
      "tenant": "t1",
      "toggles": ["*"]
    }
    

    getEdmx (tenant, toggles, service, locale) → EDMX

    Returns the EDMX document for a given service in context of the given tenant and feature toggles vector. CAP runtimes call this to get the EDMX document they return in response to OData $metadata requests.

    Arguments  
    tenant a string identifying the tenant
    toggles an array listing toggled features; ['*'] for all features
    service fully-qualified name of a service definition
    locale requested locale, i.e. as from accept-language header
    Example Usage
    POST http://localhost:4004/-/cds/model-provider/getEdmx HTTP/1.1
    Content-Type: application/json
    Authorization: Basic yves:
    
    {
      "tenant": "t1",
      "toggles": ["*"],
      "service": "CatalogService",
      "locale": "en"
    }
    

    getResources () → TAR

    Returns a .tar archive containing CSV files, I18n files, as well as native database artifacts, required for deployment to databases. DeploymentService calls that whenever it receives a subscribe or upgrade event.

    getExtensions (tenant) → CSN

    Returns a parsed CSN document containing all the extensions stored in cds.xt.Extensions for the given tenant.

    Arguments  
    tenant a string identifying the tenant

    isExtended (tenant) → true|false

    Returns true if the given tenant has extensions applied.

    Arguments  
    tenant a string identifying the tenant

    DeploymentService

    The DeploymentService handles subscribe, unsubscribe , and upgrade events for single tenants and single apps / micro-services. Actual implementation is provided through internal plugins, for example, for HANA and SQLite.

       
    Service Definition @sap/cds-mtxs/srv/deployment-service
    Service Definition Name cds.xt.DeploymentService
    Default Http Endpoint /-/cds/deployment

    Configuration

    DeploymentService is activated and configured automatically whenever the shortcut setting cds.requires.multitenancy is activated as described in Configuration.

    In addition, it can be activated — or deactivated — selectively by the like of:

      "cds": {
        "requires": {
          "cds.xt.DeploymentService": true
        }
      }
    

    Supported values as documented in Configuring Individual Services above

    Individual Config Options
    Supported Presets
    • in-sidecar — provides defaults for usage in sidecars
    • from-sidecar — shortcut for { "kind": "rest" }

    subscribe (tenant)

    Received when a new tenant subscribes.

    The implementations create and initialize required resources, that is, creating and initializing tenant-specific HDI containers in case of HANA, or tenant-specific databases in case of SQLite.

    upgrade (tenant)

    Used to upgrade a subscribed tenant.

    Implementations read the latest models and content from the latest deployed version of the application and re-deploy that to the tenant’s database.

    Drop-Creating Databases for SQLite

    In case of SQLite, especially in case of in-memory databases, an upgrade will simply drop and create a new tenant-specific database. Which means all data is lost.

    Schema Evolution for HANA

    In case of HANA, the delta to the former database layout will be determined, and corresponding CREATE TABLE, DROP-CREATE VIEW, and ALTER TABLE statements will eventually be executed without any data loss.

    unsubscribe (tenant)

    Received when a tenant is deleted.

    The implementations free required resources, that is, dispose tenant-specific HDI containers in case of HANA, or tenant-specific databases in case of SQLite.

    SaasProvisioningService

    The SaasProvisioningService is a facade for the DeploymentService to adapt to the API expected by SAP BTP’s SaaS Provisioning Service, hence providing out-of-the-box integration.

       
    Service Definition @sap/cds-mtxs/srv/cf/saas-provisioning-service
    Service Definition Name cds.xt.SaasProvisioningService
    Default Http Endpoint /-/cds/saas-provisioning

    Configuration

    SaasProvisioningService is activated and configured automatically whenever the shortcut setting cds.requires.multitenancy is activated as described in Configuration.

    In addition, it can be activated — or deactivated — selectively by the like of:

      "cds": {
        "requires": {
          "cds.xt.SaasProvisioningService": true
        }
      }
    

    Supported values as documented in Configuring Individual Services above

    Individual Config Options
    Supported Presets
    • none

    POST/DELETE tenant/<id> (…)

    —— TODO ——

    GET dependencies → [{ xsappname }]

    —— TODO ——

    upgrade (tenant) → Jobs

    —— TODO ——

    upgradeAll (tenants) → Jobs

    —— TODO ——


    Old MTX Reference

    See Reference docs for former ‘old’ MTX Services.