Share

[[{“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

package.json
{
  "name": "xsuaa-demo",
  "version": "1.0.0",
  "dependencies": {
    "express": "^4.18.0",
    "axios": "^1.6.0",
    "@sap/xsenv": "^3.4.0"
  }
}
server.js
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

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

Important: When building on macOS (ARM64), always specify the target platform for amd64.
 
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
Expected Output: You should see “XSUAA service loaded” and “Server running on port 3000” in the logs.

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

By ali