57 · SAP Destination Service

Configure the Destination Service, understand auth types, and use cds.connect.to() with destination-backed services.

Episode 57 — SAP Destination Service

What you'll learn

Why this matters

Hardcoding external system URLs and credentials is a deployment anti-pattern. The Destination Service centralises all connection parameters — URL, auth type, credentials, proxy settings — so CAP code never contains secrets, and ops teams can change targets without code deployments.

What is a Destination?

A destination is a named connection configuration stored in BTP:

{
  "Name":              "S4HANA_SYSTEM",
  "Type":              "HTTP",
  "URL":               "https://s4hana.company.com:443",
  "Authentication":    "OAuth2ClientCredentials",
  "TokenServiceURL":   "https://..../oauth/token",
  "ClientId":          "cap-integration-client",
  "ClientSecret":      "...",
  "ProxyType":         "Internet",
  "HTML5.DynamicDestination": "true"
}

Creating a destination in BTP Cockpit

  1. BTP Cockpit → Subaccount → Connectivity → Destinations → New Destination
  2. Fill: Name, Type=HTTP, URL, Authentication type
  3. Additional Properties: HTML5.DynamicDestination=true (for AppRouter forwarding)
  4. Click "Save" then "Check Connection" to verify

Binding Destination Service to CAP

# mta.yaml
resources:
  - name: destination-service
    type: org.cloudfoundry.managed-service
    parameters:
      service:      destination
      service-plan: lite

modules:
  - name: bookshop-srv
    requires:
      - name: destination-service

Reading a destination in CAP

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

module.exports = class IntegrationService extends cds.ApplicationService {
  async init () {
    this.on('READ', 'ExternalProducts', async (req) => {
      // CAP connects to a service defined in .cdsrc.json using a named destination
      const remoteService = await cds.connect.to('ExternalProductAPI')
      return remoteService.run(req.query)
    })

    await super.init()
  }
}
// .cdsrc.json
{
  "requires": {
    "ExternalProductAPI": {
      "[production]": {
        "kind":        "odata-v4",
        "destination": "S4HANA_SYSTEM",     // ← Destination name in BTP Cockpit
        "path":        "/sap/opu/odata/sap"
      },
      "[development]": {
        "kind":  "odata-v4",
        "credentials": {
          "url": "https://sandbox.api.sap.com/..."
        }
      }
    }
  }
}

Authentication types reference

Auth Type Use case Secret stored where
NoAuthentication Public APIs n/a
BasicAuthentication Legacy systems, RFC Destination (never in code)
OAuth2ClientCredentials Service-to-service Destination
OAuth2SAMLBearerAssertion Principal propagation Destination + XSUAA
OAuth2JWTBearer User context propagation Destination + XSUAA
SAMLAssertion S/4HANA on-prem Destination

Instance vs subaccount destinations

# Check which destinations a service instance provides
cf service-key destination-service destination-key | jq '.["instanceDestinations","subaccountDestinations"]'

Local dev without Destination Service

// .cdsrc.json — use direct credentials locally
{
  "requires": {
    "ExternalProductAPI": {
      "[development]": {
        "kind": "odata-v4",
        "credentials": {
          "url":     "https://sandbox.api.sap.com/s4hanacloud",
          "headers": { "APIKey": "YOUR_DEV_API_KEY" }
        }
      }
    }
  }
}

Hands-on exercise

  1. Create a destination in your BTP trial for the SAP API Business Hub sandbox
  2. Bind the Destination Service to your CAP app via mta.yaml
  3. Change your remote service config to use "destination": "MY_DEST_NAME"
  4. Deploy and verify the call succeeds without credentials in the code

Checkpoint ✓

Sign in for progress tracking & Q&A →