33 · Remote Services

Import OData metadata with cds import, connect to external services with cds.connect.to(), and use the delegate pattern.

Episode 33 — Consuming Remote Services

What you'll learn

Why this matters

CAP apps rarely live alone — they call S/4HANA OData APIs, BTP services, and partner APIs. CAP's cds.connect.to() pattern abstracts the transport so your handler code is the same whether the remote service is a real S/4HANA system or a local mock.

Step 1 — Import the external API

# Download the API definition from SAP API Business Hub (edmx format)
# Or use CDS introspection
cds import ./API_BUSINESS_PARTNER.edmx --as cds

This creates:

srv/external/
└── API_BUSINESS_PARTNER.cds    ← CDS model of the external API
// Auto-generated (excerpt)
service API_BUSINESS_PARTNER {
  entity A_BusinessPartner {
    key BusinessPartner : String(10);
    BusinessPartnerFullName : String;
    // ...
  }
}

Step 2 — Reference in your service

// srv/orders-service.cds
using { API_BUSINESS_PARTNER as bp } from './external/API_BUSINESS_PARTNER';

service OrdersService {
  entity Orders as projection on my.Orders;

  // Expose a subset of the remote entity
  entity Suppliers as projection on bp.A_BusinessPartner {
    key BusinessPartner as ID,
        BusinessPartnerFullName as name
  }
}

Step 3 — Configure the destination

// .cdsrc.json
{
  "requires": {
    "API_BUSINESS_PARTNER": {
      "[production]": {
        "kind": "odata-v2",
        "model": "srv/external/API_BUSINESS_PARTNER",
        "destination": "S4HANA_DESTINATION",    // BTP Destination name
        "path": "/sap/opu/odata/sap"
      },
      "[development]": {
        "kind": "odata-v2",
        "model": "srv/external/API_BUSINESS_PARTNER",
        "credentials": {
          "url": "https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata/sap",
          "headers": { "APIKey": "YOUR_API_KEY" }
        }
      }
    }
  }
}

Step 4 — Implement the handler (delegate pattern)

// srv/orders-service.js
module.exports = class OrdersService extends cds.ApplicationService {
  async init () {
    const bpApi = await cds.connect.to('API_BUSINESS_PARTNER')

    // Delegate all READ on Suppliers to the remote S/4HANA API
    this.on('READ', 'Suppliers', (req) => bpApi.run(req.query))

    await super.init()
  }
}

The req.query is the CQL query built from the OData request — CAP translates $filter, $select, $top, etc. and delegates them to the remote API automatically.

Mashup: combine local and remote data

this.on('READ', 'EnrichedOrders', async (req) => {
  // 1. Read local orders
  const orders = await SELECT.from('Orders')

  // 2. Get unique supplier IDs
  const supplierIds = [...new Set(orders.map(o => o.supplier_ID))]

  // 3. Fetch supplier names from S/4HANA
  const suppliers = await bpApi.run(
    SELECT.from('A_BusinessPartner')
      .where({ BusinessPartner: { in: supplierIds } })
      .columns('BusinessPartner', 'BusinessPartnerFullName')
  )

  // 4. Merge
  const supplierMap = Object.fromEntries(suppliers.map(s => [s.BusinessPartner, s.BusinessPartnerFullName]))
  return orders.map(o => ({ ...o, supplierName: supplierMap[o.supplier_ID] || 'Unknown' }))
})

Mock for development

Create a CSV file to mock the remote entity locally:

srv/external/data/
└── API_BUSINESS_PARTNER-A_BusinessPartner.csv
BusinessPartner,BusinessPartnerFullName
1000000,ACME Corporation
1000001,GlobalTech Ltd

When kind is not set (no .cdsrc.json entry for dev), CAP auto-mocks using these CSV files.

Error handling

try {
  const suppliers = await bpApi.run(SELECT.from('A_BusinessPartner').where({ BusinessPartner: id }))
  return suppliers
} catch (err) {
  if (err.statusCode === 404) return req.reject(404, `Supplier ${id} not found in S/4HANA`)
  console.error('S/4HANA call failed:', err.message)
  return req.reject(502, 'Remote service temporarily unavailable')
}

Common mistakes

Mistake Symptom Fix
Passing req not req.query Delegation error Use bpApi.run(req.query)
No mock CSV for dev Empty results in local testing Add CSV under srv/external/data/
Missing using import CDS model not found Add using { ... } from './external/...'
Destination not configured in BTP 404 in production Create the destination in BTP Cockpit

Hands-on exercise

  1. Download the SAP API Hub Business Partner API edmx and import it
  2. Expose Suppliers as a projection in your service
  3. Implement the delegate handler
  4. Add mock CSV data; verify the mashup returns order + supplier name

Checkpoint ✓

Sign in for progress tracking & Q&A →