Old MTX Reference
API reference documentation for MTX Services.
All APIs receive and respond with JSON payloads. Application-specific logic (for example, scope checks) can be added using Event Handlers.
Streamlined MTX APIs
This is the reference documentation for our old MTX implementation. To find the API reference for our new, streamlined MTX, see MTX Services Reference. Also find instructions about migrating to new MTX.
Intro & Overview
CAP provides @sap/cds-mtx
as a Node.js module published on npmjs.com.
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> HTTP/1.1
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.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> HTTP/1.1
Subscription Dependencies
GET /mtx/v1/provisioning/dependencies HTTP/1.1
Response body: Array of String
. The default implementation returns an empty array.
GET Subscribed Tenants
GET /mtx/v1/provisioning/tenant/ HTTP/1.1
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> HTTP/1.1
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 HTTP/1.1
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 non-extended 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 HTTP/1.1
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 HTTP/1.1
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 HTTP/1.1
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> HTTP/1.1
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> HTTP/1.1
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> HTTP/1.1
Returns the compiled (extended) model of the application.
GET Languages
GET /mtx/v1/metadata/languages/<tenantId> HTTP/1.1
Returns the supported languages of the (extended) model of the application.
GET Services
GET /mtx/v1/metadata/services/<tenantId> HTTP/1.1
Returns the services of the (extended) model of the application.
Diagnose API
GET Jobs
GET /mtx/v1/diagnose/jobs HTTP/1.1
Returns information about the job queue, including waiting or running jobs.
GET Memory
GET /mtx/v1/diagnose/memory HTTP/1.1
Returns information about the memory usage.
GET Container
GET /mtx/v1/diagnose/container/<tenantId> HTTP/1.1
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, for example, `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;
});
}
Appendix — Configuration
App Router
Configure your App Router as follows.
Enable token forwarding, for example:
yaml- name: approuter requires: - name: mtx-sidecar group: destinations properties: name: mtx-sidecar url: ~{url} forwardAuthToken: true
Configure a route to MTX-Sidecar with authentication data being passed on to MTX for verification.
You may have to adjust the
destination
name according to your configuration for MTX in mta.yaml or manifest.yml, for example:yamlmodules: - name: sidecar provides: - name: mtx-sidecar properties: url: ${default-url}
Old SaaS Extensibility Guide
See the old guide for Extending and Customizing SaaS Solutions.
Further Readings
SAP BTP concepts for multitenancy are described in detail: