Primary reference: https://cap.cloud.sap/docs/node.js/cds-connect Hybrid testing: https://cap.cloud.sap/docs/advanced/hybrid-testing Kyma bindings: https://cap.cloud.sap/docs/guides/deployment/to-kyma
How CAP resolves service bindings
CAP supports servicebinding.io service bindings and SAP BTP service bindings created by the SAP BTP Service Operator. On Cloud Foundry, service bindings are provided in the VCAP_SERVICES environment variable. CAP matches them to cds.requires entries in this order: service name → binding_name → tags array → kind vs label property → kind vs type property.
// VCAP_SERVICES structure (Cloud Foundry)
{
"xsuaa": [{
"name": "my-app-auth",
"label": "xsuaa",
"tags": ["xsuaa"],
"credentials": {
"clientid": "...",
"clientsecret": "...",
"url": "https://..."
}
}],
"hana": [{
"name": "my-app-db",
"label": "hana",
"tags": ["hana", "database"],
"credentials": { ... }
}]
}
Local development — cds bind (recommended)
Use cds bind to bind BTP services locally. It constructs a VCAP_SERVICES environment variable from real BTP service instances without committing credentials.
# Bind a single service instance
cds bind -2 my-app-auth
# Bind using a specific service key
cds bind -2 my-app-destination:my-destination-key
# Bind multiple services at once
cds bind -2 my-app-auth,my-app-destination,my-app-db
# Run your app with the bound services (hybrid mode)
cds watch --profile hybrid
This creates .cdsrc-private.json — add to .gitignore immediately:
echo ".cdsrc-private.json" >> .gitignore
Use cds bind --exec to run any command with the VCAP_SERVICES variable filled from your bound services — useful for running the App Router locally with real XSUAA credentials.
# Run App Router locally with real XSUAA binding
cds bind --exec -- npm start --prefix app/router
Local development — default-env.json (legacy, less secure)
Only use if cds bind is not available. Never commit this file.
{
"VCAP_SERVICES": {
"xsuaa": [{
"name": "my-app-auth",
"credentials": { ... }
}]
}
}
Add to .gitignore:
echo "default-env.json" >> .gitignore
Kyma / Kubernetes bindings
Use the ServiceBinding custom resource of the SAP BTP Service Operator to create bindings in Kyma. Specify a root directory for all service bindings using the SERVICE_BINDING_ROOT environment variable.
# servicebinding.yaml
apiVersion: services.cloud.sap.com/v1
kind: ServiceBinding
metadata:
name: my-app-xsuaa-binding
spec:
serviceInstanceName: my-app-auth
secretName: my-app-xsuaa-secret
# deployment.yaml — mount the secret as a volume
env:
- name: SERVICE_BINDING_ROOT
value: /bindings
volumeMounts:
- name: xsuaa-binding
mountPath: /bindings/auth
volumes:
- name: xsuaa-binding
secret:
secretName: my-app-xsuaa-secret
Resolving ambiguous bindings
If multiple instances of the same kind are bound, CAP warns it can't auto-resolve. Fix with vcap.name:
{
"cds": {
"requires": {
"db": {
"kind": "hana",
"vcap": { "name": "my-app-db-production" }
}
}
}
}
Accessing credentials in code
// Never hardcode credentials — always read from binding
const { credentials } = cds.requires['my-remote-service']
const { url, clientid, clientsecret } = credentials
// Or use @sap/xsenv for direct access
const xsenv = require('@sap/xsenv')
const services = xsenv.getServices({ myService: { tag: 'my-tag' } })
Common mistakes to avoid
-
❌ Committing
.cdsrc-private.jsonordefault-env.json— contains real credentials -
✅ Both files must be in
.gitignorebefore runningcds bind -
❌ Using
default-env.jsonin CI/CD pipelines — credentials in repo history -
✅ Use environment variables or secret management in pipelines
-
❌ Hardcoding
VCAP_SERVICESJSON in code -
✅ Always read credentials via
cds.requires[...].credentialsorxsenv -
❌ Binding to a service instance shared with production for local testing
-
✅ Use dedicated dev/test service instances
-
❌ Forgetting
SERVICE_BINDING_ROOTin Kyma deployments -
✅ CAP won't find the credentials without the env variable pointing to the mount path