493 lines
9.5 KiB
Markdown
493 lines
9.5 KiB
Markdown
# Development Reference
|
|
|
|
Development patterns and best practices for SAP BTP applications.
|
|
|
|
**Source**: [https://github.com/SAP-docs/sap-btp-cloud-platform/tree/main/docs/30-development](https://github.com/SAP-docs/sap-btp-cloud-platform/tree/main/docs/30-development)
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Multi-Target Applications](#multi-target-applications)
|
|
2. [Application Router](#application-router)
|
|
3. [CAP Development](#cap-development)
|
|
4. [Service Bindings](#service-bindings)
|
|
5. [CI/CD Pipelines](#cicd-pipelines)
|
|
6. [Deployment Strategies](#deployment-strategies)
|
|
|
|
---
|
|
|
|
## Multi-Target Applications
|
|
|
|
### MTA Structure
|
|
|
|
```
|
|
my-app/
|
|
├── mta.yaml # MTA descriptor
|
|
├── srv/ # Backend service
|
|
│ ├── package.json
|
|
│ └── src/
|
|
├── app/ # Frontend
|
|
│ └── webapp/
|
|
├── db/ # Database artifacts
|
|
│ └── src/
|
|
└── xs-security.json # Security config
|
|
```
|
|
|
|
### mta.yaml Template
|
|
|
|
```yaml
|
|
_schema-version: "3.1"
|
|
ID: my-app
|
|
version: 1.0.0
|
|
description: My SAP BTP Application
|
|
|
|
parameters:
|
|
enable-parallel-deployments: true
|
|
|
|
build-parameters:
|
|
before-all:
|
|
- builder: custom
|
|
commands:
|
|
- npm install --production
|
|
|
|
modules:
|
|
# Backend service
|
|
- name: my-app-srv
|
|
type: nodejs
|
|
path: srv
|
|
parameters:
|
|
buildpack: nodejs_buildpack
|
|
memory: 256M
|
|
build-parameters:
|
|
builder: npm
|
|
requires:
|
|
- name: my-app-db
|
|
- name: my-app-auth
|
|
provides:
|
|
- name: srv-api
|
|
properties:
|
|
srv-url: ${default-url}
|
|
|
|
# Database deployer
|
|
- name: my-app-db-deployer
|
|
type: hdb
|
|
path: db
|
|
parameters:
|
|
buildpack: nodejs_buildpack
|
|
requires:
|
|
- name: my-app-db
|
|
|
|
# UI module
|
|
- name: my-app-ui
|
|
type: html5
|
|
path: app
|
|
build-parameters:
|
|
builder: custom
|
|
commands:
|
|
- npm run build
|
|
supported-platforms: []
|
|
|
|
# App Router
|
|
- name: my-app-approuter
|
|
type: approuter.nodejs
|
|
path: approuter
|
|
parameters:
|
|
disk-quota: 256M
|
|
memory: 256M
|
|
requires:
|
|
- name: my-app-auth
|
|
- name: srv-api
|
|
group: destinations
|
|
properties:
|
|
name: srv-api
|
|
url: ~{srv-url}
|
|
forwardAuthToken: true
|
|
|
|
resources:
|
|
# HDI Container
|
|
- name: my-app-db
|
|
type: com.sap.xs.hdi-container
|
|
parameters:
|
|
service: hana
|
|
service-plan: hdi-shared
|
|
|
|
# XSUAA
|
|
- name: my-app-auth
|
|
type: org.cloudfoundry.managed-service
|
|
parameters:
|
|
service: xsuaa
|
|
service-plan: application
|
|
path: ./xs-security.json
|
|
```
|
|
|
|
### Build and Deploy
|
|
|
|
```bash
|
|
# Build MTA archive
|
|
mbt build
|
|
|
|
# Deploy
|
|
cf deploy mta_archives/my-app_1.0.0.mtar
|
|
|
|
# Deploy with options
|
|
cf deploy my-app.mtar --strategy blue-green
|
|
```
|
|
|
|
---
|
|
|
|
## Application Router
|
|
|
|
### Purpose
|
|
|
|
- Single entry point for applications
|
|
- User authentication
|
|
- Static content serving
|
|
- URL routing to microservices
|
|
- Session management
|
|
|
|
### xs-app.json
|
|
|
|
```json
|
|
{
|
|
"welcomeFile": "/index.html",
|
|
"authenticationMethod": "route",
|
|
"sessionTimeout": 30,
|
|
"routes": [
|
|
{
|
|
"source": "^/api/(.*)$",
|
|
"target": "$1",
|
|
"destination": "srv-api",
|
|
"authenticationType": "xsuaa",
|
|
"csrfProtection": true
|
|
},
|
|
{
|
|
"source": "^/(.*)$",
|
|
"target": "$1",
|
|
"localDir": "webapp",
|
|
"authenticationType": "xsuaa"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Authentication Types
|
|
|
|
| Type | Description |
|
|
|------|-------------|
|
|
| `xsuaa` | Require authentication |
|
|
| `none` | No authentication |
|
|
| `basic` | Basic auth (dev only) |
|
|
|
|
### Route Properties
|
|
|
|
| Property | Description |
|
|
|----------|-------------|
|
|
| `source` | Regex pattern for incoming URL |
|
|
| `target` | Rewritten path |
|
|
| `destination` | Destination name |
|
|
| `localDir` | Serve from local directory |
|
|
| `csrfProtection` | Enable CSRF tokens |
|
|
| `scope` | Required authorization scope |
|
|
|
|
### Environment Variables
|
|
|
|
```json
|
|
{
|
|
"destinations": [
|
|
{
|
|
"name": "srv-api",
|
|
"url": "[https://my-srv.cfapps.eu10.hana.ondemand.com",](https://my-srv.cfapps.eu10.hana.ondemand.com",)
|
|
"forwardAuthToken": true
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## CAP Development
|
|
|
|
### Project Setup
|
|
|
|
```bash
|
|
# Create new project
|
|
cds init my-project
|
|
|
|
# Add features
|
|
cds add hana
|
|
cds add xsuaa
|
|
cds add mta
|
|
```
|
|
|
|
### Service Definition (CDS)
|
|
|
|
```cds
|
|
// srv/catalog-service.cds
|
|
using { my.bookshop as my } from '../db/schema';
|
|
|
|
service CatalogService {
|
|
@readonly entity Books as projection on my.Books;
|
|
entity Orders as projection on my.Orders;
|
|
}
|
|
```
|
|
|
|
### Data Model
|
|
|
|
```cds
|
|
// db/schema.cds
|
|
namespace my.bookshop;
|
|
|
|
entity Books {
|
|
key ID : Integer;
|
|
title : String;
|
|
author : Association to Authors;
|
|
stock : Integer;
|
|
}
|
|
|
|
entity Authors {
|
|
key ID : Integer;
|
|
name : String;
|
|
books : Association to many Books on books.author = $self;
|
|
}
|
|
|
|
entity Orders {
|
|
key ID : UUID;
|
|
book : Association to Books;
|
|
amount : Integer;
|
|
}
|
|
```
|
|
|
|
### Service Implementation
|
|
|
|
```javascript
|
|
// srv/catalog-service.js
|
|
module.exports = cds.service.impl(async function() {
|
|
const { Books, Orders } = this.entities;
|
|
|
|
this.before('CREATE', 'Orders', async (req) => {
|
|
const { book_ID, amount } = req.data;
|
|
const book = await SELECT.one.from(Books).where({ ID: book_ID });
|
|
if (book.stock < amount) {
|
|
req.error(409, 'Not enough stock');
|
|
}
|
|
});
|
|
|
|
this.after('CREATE', 'Orders', async (order, req) => {
|
|
await UPDATE(Books)
|
|
.set({ stock: { '-=': order.amount } })
|
|
.where({ ID: order.book_ID });
|
|
});
|
|
});
|
|
```
|
|
|
|
### Running Locally
|
|
|
|
```bash
|
|
# Start with watch (SQLite in-memory)
|
|
cds watch
|
|
|
|
# With hybrid profile (remote services, local app)
|
|
cds watch --profile hybrid
|
|
|
|
# Deploy to database
|
|
cds deploy --to hana
|
|
```
|
|
|
|
**Profile Options**:
|
|
| Profile | Description | Use Case |
|
|
|---------|-------------|----------|
|
|
| `default` | SQLite in-memory, mock auth | Initial development, quick testing |
|
|
| `hybrid` | Connect to remote BTP services while running locally | Test with real HANA, XSUAA, destinations |
|
|
| `production` | Full BTP services | Deployed application |
|
|
|
|
**Hybrid Profile Setup** (`.cdsrc.json`):
|
|
```json
|
|
{
|
|
"[hybrid]": {
|
|
"requires": {
|
|
"db": {
|
|
"kind": "hana",
|
|
"credentials": { "from": "env:VCAP_SERVICES" }
|
|
},
|
|
"auth": {
|
|
"kind": "xsuaa",
|
|
"credentials": { "from": "env:VCAP_SERVICES" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Run `cds bind` to fetch service credentials, then `cds watch --profile hybrid`.
|
|
|
|
---
|
|
|
|
## Service Bindings
|
|
|
|
### Accessing Bound Services
|
|
|
|
**Environment Variable (VCAP_SERVICES)**:
|
|
```javascript
|
|
const vcap = JSON.parse(process.env.VCAP_SERVICES);
|
|
const hanaCredentials = vcap.hana[0].credentials;
|
|
```
|
|
|
|
**Using @sap/xsenv**:
|
|
```javascript
|
|
const xsenv = require('@sap/xsenv');
|
|
xsenv.loadEnv();
|
|
|
|
const hanaCredentials = xsenv.serviceCredentials({ tag: 'hana' });
|
|
```
|
|
|
|
**Using CAP**:
|
|
```javascript
|
|
// Automatic binding via cds.requires in package.json
|
|
const db = await cds.connect.to('db');
|
|
```
|
|
|
|
### package.json (CAP)
|
|
|
|
```json
|
|
{
|
|
"cds": {
|
|
"requires": {
|
|
"db": {
|
|
"kind": "hana",
|
|
"credentials": {
|
|
"binding": "db"
|
|
}
|
|
},
|
|
"auth": {
|
|
"kind": "xsuaa"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## CI/CD Pipelines
|
|
|
|
### SAP Continuous Integration and Delivery
|
|
|
|
Pipeline types:
|
|
1. Cloud Foundry - Fiori, CAP
|
|
2. SAP Fiori for ABAP Platform
|
|
3. SAP Integration Suite Artifacts
|
|
|
|
### Pipeline Configuration
|
|
|
|
```yaml
|
|
# .pipeline/config.yml
|
|
general:
|
|
buildTool: mta
|
|
mtaBuildTool: cloudMbt
|
|
|
|
stages:
|
|
Build:
|
|
npmExecuteBefore:
|
|
dockerImage: 'node:18'
|
|
|
|
Integration:
|
|
credentials:
|
|
cfCredentialsId: cf-credentials
|
|
|
|
Release:
|
|
cfSpace: prod
|
|
cfCredentialsId: cf-credentials
|
|
```
|
|
|
|
### GitHub Actions Example
|
|
|
|
```yaml
|
|
name: Deploy to BTP
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
deploy:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v3
|
|
with:
|
|
node-version: '18'
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Build MTA
|
|
run: npx mbt build
|
|
|
|
- name: Deploy to CF
|
|
env:
|
|
CF_API: ${{ secrets.CF_API }}
|
|
CF_USER: ${{ secrets.CF_USER }}
|
|
CF_PASSWORD: ${{ secrets.CF_PASSWORD }}
|
|
run: |
|
|
cf login -a $CF_API -u $CF_USER -p $CF_PASSWORD -o $CF_ORG -s $CF_SPACE
|
|
cf deploy mta_archives/*.mtar -f
|
|
```
|
|
|
|
---
|
|
|
|
## Deployment Strategies
|
|
|
|
### Rolling Deployment (Default)
|
|
|
|
Replace instances one by one:
|
|
```bash
|
|
cf push my-app
|
|
```
|
|
|
|
### Blue-Green Deployment
|
|
|
|
Zero-downtime with instant rollback:
|
|
|
|
```bash
|
|
# Deploy new version
|
|
cf push my-app-new -f manifest.yml
|
|
|
|
# Map production route
|
|
cf map-route my-app-new cfapps.eu10.hana.ondemand.com -n my-app
|
|
|
|
# Unmap from old
|
|
cf unmap-route my-app cfapps.eu10.hana.ondemand.com -n my-app
|
|
|
|
# Delete old version
|
|
cf delete my-app -f
|
|
|
|
# Rename
|
|
cf rename my-app-new my-app
|
|
```
|
|
|
|
**With MTA**:
|
|
```bash
|
|
cf deploy my-app.mtar --strategy blue-green
|
|
```
|
|
|
|
### Canary Deployment
|
|
|
|
Gradual traffic shift:
|
|
```bash
|
|
# Deploy canary with different route
|
|
cf push my-app-canary -f manifest-canary.yml
|
|
|
|
# Gradually shift traffic (manual or with load balancer)
|
|
```
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- Development Guide: [https://github.com/SAP-docs/sap-btp-cloud-platform/tree/main/docs/30-development](https://github.com/SAP-docs/sap-btp-cloud-platform/tree/main/docs/30-development)
|
|
- CAP Documentation: [https://cap.cloud.sap/docs/](https://cap.cloud.sap/docs/)
|
|
- MTA Guide: [https://help.sap.com/docs/btp/sap-business-technology-platform/multitarget-applications](https://help.sap.com/docs/btp/sap-business-technology-platform/multitarget-applications)
|