Skip to content
Search

    MTX APIs Reference

    All APIs receive and respond with JSON payloads. Application-specific logic (for example, scope checks) can be added using Event Handlers.

    Intro & Overview

    CAP provides @sap/cds-mtx as a Node.js module published on npmjs.com:

    Java SaaS application deployment

    It provides a number of APIs for implementing SaaS applications on SAP BTP. All APIs are based on CDS. They can be exposed through plain REST, and/or consumed by other Node.js modules when running on the same server as cds-mtx:

    • provisioning: Implements the subscription callback API as required by SAP BTP. If a tenant subscribes to the SaaS application, the onboarding request is handled. cds-mtx contacts the SAP Service Manager to create a new HDI container for the tenant. Database artifacts are then deployed into this HDI container. In addition, the unsubscribe operation and the “get dependencies” operations are supported.

    • metadata: Can be used to get CSN and EDMX models, and to get a list of available services and languages.

    • model: Used to extend existing CDS models and to perform tenant model upgrades after having pushed a new version of the SaaS application.

    Provisioning API

    Subscribe Tenant

    PUT /mtx/v1/provisioning/tenant/<tenantId>
    

    Minimal request body:

    {
      "subscribedSubdomain": "<subdomain>",
      "eventType": "CREATE"
    }
    

    Only if eventType is set to CREATE, the subscription is performed.

    An application can mix in application-specific parameters into this payload, which it can interpret within application handlers. Use the _application_ object to specify those parameters. There’s one predefined sap object, which is interpreted by cds-mtx default handlers. With that object, you can set service creation parameters to be used by the SAP Service Manager when creating HDI container service instances. A typical use case is to provide the database_id to distinguish between multiple SAP HANA databases mapped to one Cloud Foundry space.

    {
      "subscribedSubdomain": "<subdomain>",
      "eventType": "CREATE",
      "_application_": {
        "sap": {
          "service-manager": {
            "provisioning_parameters": { "database_id": "<HANA DB GUID>" }
          }
        }
      }
    }
    

    If you have more than one SAP HANA database mapped to one space, subscription doesn’t work out of the box, unless you’ve specified a default database.

    You can also set a default database using the cds.env.mtx.provisioning.container environment configuration.

    As the database_id is only known when deploying the application, it’s recommended to add the configuration as an environment variable in a *.mtaext file for deployment only:

     - name: bookshop-srv
       type: nodejs
       path: gen/srv
       properties:
         CDS_MTX_PROVISIONING_CONTAINER: { "provisioning_parameters": { "database_id": "<DB ID>" } }
    
    

    The provisioning_parameters specified in the request overwrite the configured provisioning_parameters.

    Unsubscribe Tenant

    DELETE /mtx/v1/provisioning/tenant/<tenantId>
    

    Subscription Dependencies

    GET /mtx/v1/provisioning/dependencies
    

    Response body: Array of String. The default implementation returns an empty array.

    GET Subscribed Tenants

    GET /mtx/v1/provisioning/tenant/
    

    Returns the list of subscribed tenants. For each tenant, the request body that was used for subscribing the tenant is returned.

    Model API

    Get CDS Model Content

    GET mtx/v1/model/content/<tenantId>
    

    Returns the two objects base and extension in the response body:

    {
      "base": "<base model cds files>",
      "extension": "<extension cds files>"
    }
    

    Activate Extensions

    POST mtx/v1/model/activate
    

    Request body (example):

    {
      "tenant": "tenant-extended",
      "extension": "<cds extension files>",
      "undeployExtension": false
    }
    

    The extension element must be a JSON array of arrays. Each first-level array element corresponds to a CDS file containing CDS extensions. Each second-level array element must be a two-entry array. The first entry specifies the file name. The second entry specifies the file content. Extension files for data models must be placed in a db folder. Extensions for services must be placed in an srv folder. Entities of the base model (the nonextended model) are imported by using ... from '_base/...'.

    If the undeployExtension flag is set, all extensions are undeployed from the database that are no longer part of the extensions in the current activation call.

    ❗ Warning undeployExtension has to be used with care as it potentially removes tables and their content from the database.

    Request body detailed sample:

    {
    "tenant": "tenant-extended",
    "extension": [
      [
      "db/ext-entities.cds",
      "using my.bookshop from '_base/db/data-model'; \n extend entity bookshop.Books with { \n ISBN: String; \n rating: Integer \n  }"
      ],
      [
      "db/new-entities.cds",
      "namespace com.acme.ext; \n entity Categories { \n key ID: String; \n description: String; \n }"
      ],
      [
      "srv/ext-service.cds",
      "using CatalogService from '_base/srv/cat-service'; \n using com.acme.ext from '../db/new-entities'; \n extend service CatalogService with { \n  @insertonly entity Categories as projection on ext.Categories; \n }"
      ]
      ],
    "undeployExtension": false
    }
    

    Deactivate Extension

    POST /mtx/v1/model/deactivate
    

    Request body (example):

    {
      "tenant": "tenant-extended",
      "extension_files": [
        "srv/ext-service.cds"
      ]
    }
    

    extension_files is an array of the files that are to be removed from the extensions.

    Use this API to deactivate extension. To activate and deactivate an extension in one call, use activate with undeployExtension: true.

    ❗ Warning The API has to be used with care as it removes tables and their content from the database.

    Reset Extension

    POST /mtx/v1/model/reset
    

    Request body (example):

    {
      "tenant": "tenant-extended"
    }
    

    Use this API to remove all extensions.

    ❗ Warning The API has to be used with care as it removes tables and their content from the database.

    Upgrade Base Model from Filesystem (Asynchronous)

    POST mtx/v1/model/asyncUpgrade
    

    Request body:

    {
      "tenants": ["tenant-extended-1", "tenant-non-extended-2", ...],
      "autoUndeploy": <boolean>
    }
    

    Upgrade all tenants with request body { "tenants": ["all"] }.

    If autoUndeploy is set to true, the auto-undeploy mode of the HDI deployer is used. See HDI Delta Deployment and Undeploy Allow List for more details.

    Response (example):

    { "jobID": "iy5u935lgaq" }
    

    You can use the jobID to query the status of the upgrade process:

    GET /mtx/v1/model/status/<jobID>
    

    During processing, the response can look like this:

    {
      "error": null,
      "status": "RUNNING",
      "result": null
    }
    

    Once a job is finished, the collective status is reported like this:

    {
      "error": null,
      "status": "FINISHED",
      "result": {
          "tenants": {
              "<tenantId1>": {
                  "status": "SUCCESS",
                  "message": "",
                  "buildLogs": "<build logs>"
              },
              "<tenantId2>": {
                  "status": "FAILURE",
                  "message": "<some error log output>",
                  "buildLogs": "<build logs>"
              }
          }
      }
    }
    

    The status of a job can be QUEUED (not started yet), RUNNING, FINISHED, or FAILED.

    The result status of the upgrade operation per tenant can be RUNNING, SUCCESS, or FAILURE.

    Logs are persisted for a period of 30 minutes before they get deleted automatically. If you request the job status after that, you’ll get a 404 Not Found response.

    Metadata API

    All metadata APIs support eTags. By setting the corresponding header, you can check for model updates.

    GET EDMX

    GET /mtx/v1/metadata/edmx/<tenantId>
    

    Returns the EDMX metadata of the (extended) model of the application.

    Optional URL parameters

    name=<service-name>
    language=<language-code>
    

    GET CSN

    GET /mtx/v1/metadata/csn/<tenantId>
    

    Returns the compiled (extended) model of the application.

    GET Languages

    GET /mtx/v1/metadata/languages/<tenantId>
    

    Returns the supported languages of the (extended) model of the application.

    GET Services

    GET /mtx/v1/metadata/services/<tenantId>
    

    Returns the services of the (extended) model of the application.

    Diagnose API

    GET Jobs

    GET /mtx/v1/diagnose/jobs
    

    Returns information about the job queue, including waiting or running jobs.

    GET Memory

    GET /mtx/v1/diagnose/memory
    

    Returns information about the memory usage.

    GET Container

    GET /mtx/v1/diagnose/container/<tenantId>
    

    Returns information about a tenant’s HDI container.

    Adding Custom Handlers


    If you’re using a CAP Java server, it re-exposes the APIs required by SAP BTP’s SaaS Manager (the Provisioning API). We recommended leveraging the corresponding Java-based mechanisms to add handlers to these APIs. Handlers for the Model-API of cds-mtx must always be implemented on the Node.js server, because this API isn’t re-exposed by the CAP Java runtime.


    cds-mtx APIs are implemented as CDS services. Therefore, service implementations can be overridden using CDS event handlers. For cds-mtx APIs, custom handlers have to be registered on the mtx event in a custom server.js:

    const cds = require('@sap/cds')
    
    cds.on('mtx', async () => {
      const provisioning = await cds.connect.to('ProvisioningService')
      provisioning.prepend(() => {
        provisioning.on('UPDATE', 'tenant', async (req, next) => {
          await next() // default implementation creating HDI container
          return '<bookshop-srv-url>/admin'
        })
      })
    })
    

    See the following use cases for more examples.

    Use Case: Implement a Tenant Provisioning Handler

    You can set an application entry point for the subscription in the SAP BTP Cockpit (usually a UI).

    Create a provisioning.js file in the srv folder:

    module.exports = (service) => {
      service.on('UPDATE', 'tenant', async (req, next) => {
        await next() // default implementation creating HDI container
        return '<bookshop-srv-url>/admin'
      })
    }
    

    In the provided code sample, you have to replace <bookshop-srv-url> with the URL of your bookshop-srv application on Cloud Foundry. In this example, the /admin endpoint is returned. It’s important that this endpoint isn’t protected (doesn’t require a JWT token).

    Custom code after asynchronous provisioning can be invoked with handlers for the internal endpoint to create tenants. This endpoint is called for both synchronous and asynchronous provisioning:

    module.exports = (service) => {
      service.on('createTenant', async (req, next) => {
        await next() // default implementation creating HDI container
        const { subscriptionData } = req.data // original request payload
        // custom code
        return '<bookshop-srv-url>/admin'
      })
    }
    

    Use Case: Handler for Tenant Upgrade

    To execute custom code for tenant upgrades (see also Tenant upgrade API), you can add handlers for the upgrade API that is called by cds-mtx. This API is called for the synchronous, as well as the asynchronous upgrade for each tenant.

    module.exports = (service) => {
      service.on('upgradeTenant', async (req, next) => {
        await next() // call the upgrade
        const {
          instanceData, // HDI container metadata
          deploymentOptions // additional deployment options, e.g. `autoUndeploy`
        } = cds.context.req.body
        // custom code
      })
    }
    

    Use Case: Handler for Database Deployment

    To add custom code to the deployment of your application model to the SAP HANA database, you can add handlers for the deployment API called by cds-mtx.

    This example dynamically adds an additional SAP HANA service to the environment, so it can be used through synonyms (see also Enable Access to Objects in Another HDI Container):

    module.exports = (service) => {
      service.before('deployToDb', async (context) => {
        const {
          sourceDir, // directory with generated SAP HANA sources
          instanceData, // HDI container metadata
          deploymentOptions // additional deployment options, for example, `autoUndeploy`
        } = cds.context.req.body;
        // ...
        const hana = [{
    	    "label": "hana",
    	    "provider": null,
    	    "plan": "hdi-shared",
    	    "name": "common-db-sample",
    	    "<custom-key>": "value"
        }];
        context.data.additionalServices.hana = hana;
      });
    

    Further Readings

    SAP BTP concepts for multitenancy are described in detail: