| name | messaging |
| description | Use when implementing event-driven patterns in SAP CAP: emitting or consuming events, SAP Event Mesh, CloudEvents format, messaging configuration, topic names, the outbox pattern, or cross-service event communication.
|
| metadata | {"category":"cap","version":"1.0.0","keywords":["SAP Event Mesh","CloudEvents","event","emit","consume","outbox","messaging","pub/sub","enterprise-messaging","topic","async"],"related":{"service-handlers":"emit events from service handlers","btp-deployment":"Event Mesh service binding in mta.yaml","multitenancy":"tenant-aware messaging"}} |
Messaging — CAP Best Practices
Primary reference: https://cap.cloud.sap/docs/guides/messaging/
Event Mesh: https://cap.cloud.sap/docs/guides/messaging/event-mesh
Defining events in CDS
// In the emitting service
service OrderService {
entity Orders as projection on db.Orders;
// Declare events (these become CloudEvents topics)
event OrderSubmitted {
orderID : UUID;
customer : String;
amount : Decimal(9,2);
currency : String(3);
}
event OrderShipped {
orderID : UUID;
trackingNr : String;
}
}
Emitting events
module.exports = class OrderService extends cds.ApplicationService {
async init() {
this.on('submitOrder', this.onSubmitOrder)
return super.init()
}
async onSubmitOrder(req) {
const { orderID } = req.data
await this.emit('OrderSubmitted', {
orderID,
customer: req.user.id,
amount: order.totalAmount,
currency: order.currency_code
})
return { success: true }
}
}
Consuming events from another service
module.exports = class ShippingService extends cds.ApplicationService {
async init() {
const OrderSrv = await cds.connect.to('OrderService')
OrderSrv.on('OrderSubmitted', this.onOrderSubmitted.bind(this))
return super.init()
}
async onOrderSubmitted({ data }) {
const { orderID, amount } = data
console.log(`Preparing shipment for order ${orderID}`)
}
}
Messaging Kinds — Choosing the right provider
| Kind | Protocol | Best for |
|---|
enterprise-messaging | AMQP | Legacy, backward compatibility |
event-mesh | HTTP/webhooks | Production cloud deployments |
event-mesh-shared | AMQP | Shared/hybrid scenarios, multitenant apps |
Quick setup (CDS 10+):
cds add event-mesh
cds add event-mesh-shared
SAP Event Mesh configuration
package.json / .cdsrc.json:
{
"cds": {
"requires": {
"messaging": {
"kind": "enterprise-messaging",
"format": "cloudevents"
}
}
}
}
Bind the enterprise-messaging service instance in mta.yaml:
- name: my-cap-app-em
type: org.cloudfoundry.managed-service
parameters:
service: enterprise-messaging
service-plan: default
path: ./em-config.json
em-config.json (Event Mesh descriptor):
{
"emname": "my-cap-app-em",
"namespace": "my/company/app",
"version": "1.1.0",
"options": { "management": true, "messagingrest": true },
"rules": {
"topicRules": {
"publishFilter": ["*"],
"subscribeFilter": ["*"]
},
"queueRules": {
"publishFilter": ["*"],
"subscribeFilter": ["*"
Topic naming (CloudEvents)
CAP auto-derives the topic from the service and event name:
sap/cap/{namespace}/{service}/{event}
For example: sap/cap/com.acme/OrderService/OrderSubmitted
You can override:
event OrderSubmitted @(topic: 'com/acme/orders/submitted/v1') { ... }
The Outbox pattern (transactional safety)
CAP 7+ uses the outbox by default — events are written to the DB in the same transaction as your data writes, then delivered asynchronously. No lost events on crash.
{
"cds": {
"requires": {
"outbox": true
}
}
}
To opt out (fire-and-forget):
this.emit('OrderSubmitted', data, { queue: false })
Local development (file-based mock)
{
"cds": {
"requires": {
"messaging": {
"kind": "file-based-messaging"
}
}
}
}
Events are written to ~/.cds-msg-box/ — no Event Mesh instance needed locally.
Common mistakes to avoid
- ❌ Emitting events before the DB transaction commits (use the outbox!)
- ❌ Forgetting to declare events in CDS — type safety and documentation matter
- ❌ Subscribing in
cds.on('bootstrap', ...) instead of service.init() — order matters
- ❌ Using generic topic names — namespace them to avoid collisions across apps
- ❌ Not handling duplicate delivery — Event Mesh delivers at-least-once; make consumers idempotent
- ❌ Hardcoding the Event Mesh namespace in code — derive from service binding at runtime