Destinations
Many applications on SAP Business Technology Platform (SAP BTP) communicate with other systems, e.g. line of business solutions. SAP BTP provides the concept of destinations for convenient communication between SAP BTP and other systems. A destination is an object with the following information, among others:
- URL
- authentication configuration
- proxy settings
Destinations are managed separately from applications on SAP BTP and can be retrieved through the destination service at runtime. The reasons to separate destinations and application are manifold:
- You can securely store authentication information that should not be part of application code.
- You can update resource locations without touching application code.
- Different customers may want to configure different systems.
- Multiple applications might want to access the same systems.
The SAP Cloud SDK helps you to authenticate against the destination service and retrieve destinations into your application.
The SAP Cloud SDK supports OData and OpenApi services.
For both service types, the execute()
method triggers a request to a target system.
For OData services, you can invoke the method as:
- SDK 2.x
- SDK 1.x
const { businessPartnerApi } = businessPartnerService();
const responseOData = await businessPartnerApi
.requestBuilder()
.getAll()
.execute(myDestination);
const responseOData = await BusinessPartner.requestBuilder()
.getAll()
.execute(myDestination);
Similarly for OpenAPI:
const responseOpenApi = await MyApi.myFunction().execute(myDestination);
myDestination
is an SAP Cloud SDK representation of a destination.
The given request is executed against myDestination
.
For the rest of the document, we will discuss the different options on destination lookup. All statements equally apply to OData and OpenAPI based services.
Creating Destinations Manually
You can construct a destination object manually and pass the destination information directly to the execute()
method.
The minimal requirement towards a manually constructed destination is that you provide a url
.
.execute({ url: 'https://example.com'})
This, however, is not recommended for productive use as you would loose the benefits of separating destinations from applications.
Referencing Destinations by Name
Instead of defining your destination manually, you can reference it by a name:
.execute({ destinationName: 'myDestination' })
The SAP Cloud SDK searches for the destination by its name in the following locations and in the following order:
- local environment variables
- register a destination in the SAP Cloud SDK
- service binding environment variables
- SAP BTP's destination service
The search stops, once a destination is found. Therefore, if a destination with the same name exists in multiple locations, the destination that occurs earliest during the search takes precedence before other destinations with the same name.
note
The second option register a destination
was introduced in version 2.0 of the SAP Cloud SDK.
Local Environment Variable
This option is present for deployment and testing in a local environment outside the SAP BTP where no destination services are available.
If a destinations
environment variable is present it will be used for the destination lookup as described above.
The value is expected to be a stringified JSON array, where the items adhere to the Destination interface.
Example: "[{\"name\": \"TESTINATION\", \"url\": \"http://url.hana.ondemand.com\", \"username\": \"DUMMY\", \"password\": \"dummy\"}]"
.
The SAP Cloud SDK provides a setTestDestination(destination)
function to add a destination to the environment variable for the current process programmatically.
It takes a destination object, transforms it to a JSON object, and adds it to process.env.destinations
.
import { setTestDestination } from '@sap-cloud-sdk/test-util';
setTestDestination({
authentication: 'NoAuthentication',
name: 'TESTINATION',
isTrustingAllCertificates: false,
url: 'https://mys4hana.com'
});
This would set a destination with the name TESTINATION
.
At runtime, the SAP Cloud SDK will check the environment variable for a destination with the given name and use it if present.
Note that the SAP Cloud SDK offers also a mockTestDestination()
method, which reads in a systems.json
and credentials.json
to create destinations.
The advantage of files is that they can be excluded from the repository since they contain sensitive information.
Note that this approach is not suitable for multi-tenant scenarios, because only the destination service can return destinations for different tenants.
Register Destination via the SAP Cloud SDK
Caution
This functionality was introduced in version 2.0 of the SAP Cloud SDK.
Usually, the destination service should be used in production to fetch a destination including an authentication token.
In the case of frequent service to service communication, requesting the destination service every time for a target-specific JWT
would mean a big overhead.
Also in cases where the destination needs no authentication at all the detour over the destination service is sometimes not desirable.
A solution for such use-cases is to register your destination in the SAP Cloud SDK cache:
import { registerDestination } from '@sap-cloud-sdk/connectivity';
const destination = {
name: 'MY-DESTINATION',
url: 'https://mys4hana.com'
};
registerDestination(destination, options);
MyRequest.execute({ destinationName: 'MY-DESTINATION' });
If a registered destination is found the lookup is stopped and the call to the destination service is avoided. You can register a full destination object including authentication, but we do not recommend to store authentication information in registered destination.
Instead, we recommend to enable token forwarding on the destination. If you enable this option the token used to execute the request is forwarded to the destination:
const destination = {
name: 'FORWARD-DESTINATION',
url: 'https://mys4hana.com',
forwardAuthToken: true
};
registerDestination(destination, options);
MyRequest.execute({
destinationName: 'FORWARD-DESTINATION',
jwt: 'forwardedJwt'
});
This only works if your target system accepts the unchanged JWT. If a transformation is needed e.g. OAuth to SamlBearer you need to use the destination service for that.
Note that the registerDestination
method is tenant aware.
We have a separate guide on the cache options used by the registerDestination
methods.
Service Binding Environment Variables
The service credentials, in other words VCAP_SERVICES
environment variables, may contain a destination.
If you want to use this information, the name of the service instance must be provided as the destination name.
Currently, two services types are supported out of the box business-logging
and s4-hana-cloud
with the following transformation functions:
//business-logging
(serviceBinding) => {
url: serviceBinding.credentials.writeUrl,
authentication: 'OAuth2ClientCredentials',
username: serviceBinding.credentials.uaa.clientid,
password: serviceBinding.credentials.uaa.clientsecret
};
//s4-hana-cloud
(serviceBinding) => {
url: serviceBinding.credentials.URL,
authentication: 'BasicAuthentication',
username: serviceBinding.credentials.User,
password: serviceBinding.credentials.Password
};
Destination Service
In a productive environment, you will use a Destination service to retrieve destinations.
Authentication and JSON Web Token Retrieval
To access the destination service, the SAP Cloud SDK will first fetch an access token from the XSUAA service. The token retrieved from the XSUAA service is used to make a call to the destination service and receive the destinations. The destination service returns a destination with all relevant authentication information depending on the used authentication flow:
PrincipalPropagation
NoAuthentication
BasicAuthentication
OAuth2SAMLBearerAssertion
OAuth2UserTokenExchange
OAuth2ClientCredentials
OAuth2Password
ClientCertificateAuthentication
OAuth2JWTBearer
The SAP Cloud SDK automatically parses the response of the destination service and uses the provided authentication information for the request to the target system. For a simple service, this would be the end of the story.
Multi-Tenancy
However, the destination service is special in a way that it is a tenant aware service
.
Such services make it possible to build multi-tenant applications.
So, what defines a tenant aware service?
Assume you want to build a simple application showing the 5 newest business-partner in an SAP S/4HANA system.
You want to offer this application as a service to customers.
Of course, you want to make this service cost-efficient and host it only once and let multiple customers use it.
This leads now naturally to the requirement that your service needs to return the data related to the specific customers.
A customer is represented by an account on the SAP BTP and a service considering the account is a tenant aware service
.
Tenant aware services on the SAP BTP are offered to customers via a subscription
which works on a high level as follows:
If a customer wants to use a service, a subscription is created linking the customer account and the one account hosting the service.
In the following the term subscriber account
will be used for the accounts using a service and provider account
for the one hosting it.
After this little definition detour, let's go back to the destination service and the SAP Cloud SDK. For simplicity an optional argument of the destination lookup has been neglected in the beginning:
.execute({ destinationName: 'myDestination', jwt: 'yourJWT',})
The jwt
argument takes the JSON web token (JWT) issued by an XSUAA as input.
Additional information on how to retrieve JWTs can be found here.
This token contains a field zid
holding the tenant id which will be used in the lookup process.
The lookup process done by the SAP Cloud SDK involves the following steps:
- Request an access token for the destination service and a given tenant id from the XSUAA.
- Due to the subscription between provider and subscriber, the XSUAA is allowed to issue the token.
- The token allows for calling the destination service on behalf of the given tenant. The tenant and service information is encoded in the access token.
- Make a call to the destination service using the obtained access token.
- The destination maintained in the given tenant are returned.
If no token is given or the destination is not found in the subscriber account the provider account is used as a fallback. To control this fallback behavior a selection strategy can be passed to the destination lookup:
- SDK 1.x
- SDK 2.x
.execute({ destinationName: 'myDestination', jwt: 'yourJWT', selectionStrategy: 'alwaysSubscriber' })
.execute({ destinationName: 'myDestination', jwt: 'yourJWT' }, { selectionStrategy:'alwaysSubscriber' })
There are three selection strategies defined in the SAP Cloud SDK: alwaysSubscriber
, alwaysProvider
and subscriberFirst
.
The selection strategy can be passed as an optional argument to the .execute()
method.
The default value is subscriberFirst
.
The selection strategies can be used to control for which account a destination lookup is attempted:
AlwaysSubscriber
: Only try to get destinations from the subscriber account. A valid JWT is mandatory to receive something.AlwaysProvider
: Only try to get destination from the provider account. A JWT is not needed. Even if you present a subscriber JWT the provider destination will be returned if present.SubscriberFirst
: Tries to get from the subscriber first using the JWT. If no valid JWT is provided or the destination is not found it tries the provider as described forAlwaysProvider
.
note
One aspect has been left out for simplicity.
In principle, it is possible to define destinations not only on the account level but also on the destination service level.
These destinations are called instance destinations
since they are tied to a service binding called instance on SAP BTP.
In every request, these destinations are added to the destinations returned by the destination service.
Destination Lookup Without a JSON Web Token
There are situations where you do not have a JWT issued by the XSUAA but need to look a destination up e.g. in background processes.
In such situations the property iss
of the DestinationAccessorOptions
can be used:
- SDK 1.x
- SDK 2.x
.execute({ destinationName: 'myDestination', iss: yourIssuerValue })
.execute(
{ destinationName: 'myDestination' },
{ iss: yourIssuerValue }
)
The value for iss
is supposed to be the same as in a JWT issued from the XSUAA e.g. https://yourSubdomain.localhost:8080/uaa/oauth/token
.
In principle only the host of the URL is relevant but since the same parsing and replacement methods are used for the JWT handling, the URL has to be provided in the format above.
note
If a JWT is used in the destination lookup, a validation of the provided token is performed.
This validation ensures that the JWT has not been manipulated.
If only the iss
is provided no such validation is performed.
Note that the given subdomain value defines from which tenant destinations are fetched.
So a wrong value may break the isolation for tenants.
It is your responsibility as a developer to ensure that the provided value for the iss
property is valid.
Destination Fetch Options
note
This section is written for version 2 of the SAP Cloud SDK.
Most of the options are also available in version 1 but not all e.g. timeout
.
The execute()
, getDestination()
or executeHttpRequest()
perform a destination lookup by name as discussed above.
You can pass options to adjust how the destination is fetched.
In the section above we listed already a few of the options but this sections gives a comprehensive overview:
destinationName
: The name of the destination to be fetched. This is the only mandatory property, all the other parameters are optional.jwt
: The JSON web token. Crucial in multi tenant scenarios or for user dependent authentication flows.iss
: Issuer URL which can be used to obtain destination for a subscriber tenant if no JWT is present. Read the detailed documentation above before using this option.selectionStrategy
: Specifies the order in which accounts are searched for a destination. Default issubscriberFirst
. Alternative values arealwaysProvider
andalwaysSubscriber
.iasToXsuaaTokenExchange
: Switches on token exchange form IAS format tokens to XSUAA if needed using the@sap/xssec
library. Default value is true.cacheVerificationKeys
: Switches on caching for the verification certificates for the JWT. Default value is true.useCache
: Switches on caching for destinations received from the destination service. Default value is false.isolationStrategy
: Specifies how the destination cache is scoped. The value is automatically set but under certain conditions you may want to optimize it.enableCircuitBreaker
: Switches on circuit breakers to protects the calls to theXSUAA
anddestination-service
. Default value is true.timeout
: Sets the timeout for the calls to SAP BTP services likeXSUAA
anddestination-service
. The value is in milliseconds and the default value is 10000 (10 seconds). There is a second timeout options on the request level, setting the timeout for the calls to the destination target.
Additional Headers and Query Parameters on Destinations
The destination service has a convention to define static headers and query parameters on destinations. Create additional properties in your destination in the SAP BTP cockpit and define their values as follows:
URL.headers.<header-key>
for headersURL.queries.<query-key>
for query parameters
Replace <header-key>
and <query-key>
with the name of your headers or query parameters and set the respective values.

In the example above, the destination has an apiKey
header with the value <my-api-key>
and a language
query parameter with the value EN
.
The SAP Cloud SDK adds those additional headers and query parameters for every communication with the given destination.
Rules of Precedence
The SAP Cloud SDK adds headers and query parameters from different sources. Some sources take precedence over others (highest to lowest):
- custom: headers/query parameters added to a request directly
- additional properties: headers/query parameters defined on a destination
- internal: headers/query parameters built by the SAP Cloud SDK
Headers or query parameters built by the SAP Cloud SDK are overwritten by additional headers and query parameters on the destination. Custom headers and query parameters, however, overwrite the additional properties.
note
Header names keep their casing but overwrite other headers independent of casing, e.g. AUTHORIZATION
overwrites authorization
.
This does not apply to query parameter names`.