[[{“value”:”
Introduction
This guide walks through migrating a Node.js application secured with XSUAA from SAP Cloud Foundry to SAP Kyma on SAP BTP.
Cloud Foundry support in SAP BTP trial accounts has become increasingly limited. The ‘cf push’ capability is no longer available, and quotas are restricted, making it difficult to deploy meaningful applications. Given these constraints, I’ve found it more useful to deploy to Kyma instead. Unlike Cloud Foundry’s fully managed approach, Kyma offers Kubernetes-native deployment with direct control over your containers, networking, and scaling. This guide walks through migrating a Cloud Foundry application to Kyma on BTP trial.
Prerequisites
- SAP BTP subaccount with Kyma enabled
- Docker installed (with buildx support for cross-platform builds)
- kubectl configured for your Kyma cluster
- An existing XSUAA service instance on BTP with valid credentials
- Node.js application using Express and XSUAA authentication
Step 1: Prepare Your Node.js Application
{
"name": "xsuaa-demo",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.0",
"axios": "^1.6.0",
"@sap/xsenv": "^3.4.0"
}
}
const express = require('express');
const axios = require('axios');
const xsenv = require('@sap/xsenv');
const app = express();
const PORT = process.env.PORT || 3000;
let xsuaaService;
try {
const services = xsenv.getServices({ xsuaa: { tag: 'xsuaa' } });
xsuaaService = services.xsuaa;
console.log('XSUAA service loaded');
} catch (err) {
console.error('Failed to load XSUAA:', err.message);
process.exit(1);
}
async function getAccessToken() {
const response = await axios.post(xsuaaService.url, 'grant_type=client_credentials&response_type=token', {
auth: { username: xsuaaService.clientid, password: xsuaaService.clientsecret },
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
return response.data.access_token;
}
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
app.get('/token', async (req, res) => {
try {
const token = await getAccessToken();
res.json({ access_token: token, token_type: 'bearer' });
} catch (err) {
res.status(401).json({ error: 'Failed to get token' });
}
});
app.listen(PORT, () => {
console.log('Server running on port ' + PORT);
});
Step 2: Create Dockerfile
FROM node:18 WORKDIR /usr/src/app COPY package*.json ./ RUN npm install --production COPY . . EXPOSE 3000 CMD ["node", "server.js"]
Step 3: Build and Push Image
docker buildx build --platform linux/amd64 -t your-registry/xsuaa-demo:latest --push .
Step 4: Deploy to Kyma
kubectl create deployment xsuaa-demo --image=your-registry/xsuaa-demo:latest -n default kubectl expose deployment xsuaa-demo --port=3000 --type=LoadBalancer -n default
Step 5: Configure XSUAA Credentials
kubectl create secret generic vcap-services -n default
--from-literal=VCAP_SERVICES='{"xsuaa":[{"credentials":{"clientid":"sb-xsuaa-demo!tXXXXXX","clientsecret":"xxxxxxxx-xxxx...","url":"https://mysubaccount.authentication.us10.hana.ondemand.com/oauth/token"},"label":"xsuaa","tag":"xsuaa"}]}'
Step 6: Set Environment Variables
kubectl set env deployment/xsuaa-demo VCAP_SERVICES='{"xsuaa":[{"credentials":{"clientid":"sb-xsuaa-demo!tXXXXXX","clientsecret":"xxxxxxxx-xxxx...","url":"https://mysubaccount.authentication.us10.hana.ondemand.com/oauth/token"},"label":"xsuaa","tag":"xsuaa"}]}' -n default
kubectl rollout restart deployment/xsuaa-demo -n default
Step 7: Verify and Test
kubectl logs -f deployment/xsuaa-demo -n default
kubectl get svc xsuaa-demo -n default
EXTERNAL_IP=$(kubectl get svc xsuaa-demo -n default -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
curl http://$EXTERNAL_IP:3000/health
curl http://$EXTERNAL_IP:3000/token
Key Differences: Cloud Foundry vs Kyma
Aspect Cloud Foundry Kyma (Kubernetes)
| Runtime Management | Fully managed by CF | Managed by Kubernetes operators |
| Service Binding | Automatic via VCAP_SERVICES | Manual via Kubernetes secrets/env vars |
| Deployment Unit | Application droplet | Container image |
| Scaling | CF managed | Kubernetes HPA |
| Network Policies | Limited | Full Kubernetes NetworkPolicy support |
Troubleshooting
1. Docker Build Issues on macOS
Error: “exec format error” in pod logs means you built for wrong architecture.
docker buildx build --platform linux/amd64 -t your-registry/xsuaa-demo:latest --push .
2. XSUAA Service Not Loading
Check for “Failed to load XSUAA service” in logs:
kubectl exec deployment/xsuaa-demo -n default -- env | grep VCAP_SERVICES # Output should show credentials, not placeholder values
3. Token Request Fails with getaddrinfo ENOTFOUND
VCAP_SERVICES still contains placeholder values. Re-apply with actual credentials from BTP.
4. Pod Status Stuck on PodInitializing
If using Istio, disable sidecar injection:
kubectl patch deployment xsuaa-demo -n default -p '{"spec":{"template":{"metadata":{"annotations":{"sidecar.istio.io/inject":"false"}}}}}'
kubectl rollout restart deployment/xsuaa-demo -n default
5. LoadBalancer Not Getting External IP
Use port-forward as alternative:
kubectl port-forward svc/xsuaa-demo 3000:3000 -n default
Conclusion
Migrating an XSUAA-secured application from SAP Cloud Foundry to Kyma provides greater flexibility while maintaining strong authentication standards. This approach enables seamless integration with other Kubernetes-based services on SAP BTP while leveraging XSUAA for secure API access.
Next Steps
- Implement additional endpoints for your business logic
- Add role-based access control (RBAC) using XSUAA scopes
- Configure Kubernetes HPA for automatic scaling
- Set up monitoring and logging with Kyma’s observability stack
- Implement CI/CD pipelines using SAP Continuous Integration and Delivery
“}]]
Read More Technology Blog Posts by Members articles
#abap