Skip to Content
Technical Articles

SAP Cloud Application Programming Model and Enterprise Messaging [2]: Send message

With other words:

How to create a
CAP Application
which sends messages to
Enterprise Messaging
in SAP Cloud Platform

Quicklinks:
Create CAP app
Deploy and Test
All Project Files

In the previous blog we’ve prepared ourselves and the service instance of SAP Cloud Platform Enterprise Messaging
Actually, there wasn’t much to do: just create the service instance, that’s all
We learned how to open the Dashboard and create queue and subscription

In this blog we’re doing the first step to use Enterprise Messaging from within a CAP application
As usual, I like to use most simplistic example  to show the functionality

So what we want to achieve is:
Create a CAP based application which sends messages to Enterprise Messaging
In a the next tutorial step, we deploy a second application which will receive those messages

This example is written in Node.js, however, in Java it would be similar

Note:
The messaging-integration in CAP is currently in BETA-mode
The code described below is valid from version 3.30 onward
If you need to work with old version, see appendix for legacy api

Overview

Prerequisites
Create CAP application
Deploy and Test

Prerequisites

You have access to SAP Cloud Platform,
Both Trial account and productive account can be used. It doesn’t make a difference on CAP-side

You have gone through the previous tutorial and created messaging instance with name “msgcustcare” and service plan “default”

You have made yourself familiar with CAP

Create CAP Application

Since you are familiar with CAP projects, I’m only mentioning the few steps required to send messages to Enterprise Messaging with CAP.

Note that below description is valid for CAP version from 3.30 onward.
The old API can be viewed in the appendix

See the appendix section for full content of files

manifest.yml

The first step is to bind the CAP application to the instance of Enterprise Messaging, which we created earlier
As such, we add the service instance name to our manifest.yml file

applications:
- name: capsender
  . . .
  services:
    - msgcustcare

As usual, make sure to adapt the host name to any unique name, in case it is already taken

package.json

We need 2 little additions to “normal” package.json of CAP applications:

1)
To use the messaging support of CAP, we need the following dependency

    "dependencies": {
        "@sap/xb-msg-amqp-v100": "latest",

This is a client library provided by Enterprise Messaging
It can be used to receive/send messages from/to Enterprise Messaging
This client is used by CAP under the hood, so the dependency is required

2)
In addition, we need to add small configuration for messaging:

  "cds": {
    "requires": {
      "messaging": {
        "kind": "enterprise-messaging"
      }
    }
  },

This snippet instructs CAP (roughly speaking) to connect to the Enterprise Messaging instance, which is configured in manifest.yml file.

sender-service.cds

I like to keep first samples very minimalistic, to stay focused on the very basic required steps
As such, our CAP project doesn’t contain any data-model.
Only a service model.
And in service model, we don’t need any “Entity”, we only need a “function”, which can be used to send messages:

service SenderService {
    function send() returns String;

sender-service.js

We need a custom handler to write the implementation of the “function”
This is the interesting section of this blog

Which code is required to send messages to enterprise messaging?

First: compose the message

In our first example, we just want to send a simple message.

Note:
The CAP framework has one restriction:
The message has to be a JSON object.

So in our sample code, we construct some arbitrary message.

  const message = { 
    'myProp': 'Sending message. Current Time: ' + new Date().toLocaleString()
  }

Second: The target

We need to specify to which target we want to send our message
Now we need more detailed explanation, otherwise we get crazy if the message doesn’t reach the broker

Background:
In the manifest, we’ve declared a binding to the service instance of Enterprise Messaging
After deployment, our app receives the connection data in the environment variables
The CAP framework reads it and picks the URL credentials of the broker
But, as we learned in the intro-blog, there can be multiple queues and topics defined in the messaging dashboard.
As such, in the code we have to specify to which queue (more precise: topic) we want to send our silly message

const topic = 'company/customer/care/demo/customer/created'

BUT: how to construct the topic?

Here we have to consult the messaging dashboard:
Whoever created the messaging instance, has defined some settings which we need to consider

Note:
This description is only valid in productive account, for messaging service plan “default”

1. Namespace

The namespace is defined when service instance of Enterprise Messaging is created, in the JSON parameters (see intro)
If we didn’t create the service instance, we don’t have the JSON params.
But we can check the “Service Descriptor” tab in the dashboard

2. Topic Rules

Once we’ve figured out the “namespace”, we need to check the “topic rules”
The Service Descriptor contains a section “rules”:

In our example, we want to “send” messages with our application.
With other words: we want to “publish”
And we want to publish to a “topic”
As such, we have to check the corresponding rule
In our example, a very common pattern, the rule (as defined on instance creation) requires that a topic starts with the namespace

3. Prepare Enterprise Messaging

In messaging dashboard, we need a “queue” and a “subscription”
Otherwise we cannot send messages

3.1. Queue

We need to manually create a queue and we need to follow the “queue rules”, as defined in the Service Descriptor.
In our example, the rule is the same:
queue name has to start with namespace
Note:
The creation dialog automatically adds the namespace as prefix
We can specify any arbitrary name, it has no effect on our CAP application code

3.2. Topic

After queue creation, we need a subscription to a “topic”
This time we need to manually type the full name of the topic, including namespace
In our example, let’s choose a topic, which is intended for newly created customers
(in our example, this name can be freely chosen by us. In other examples, it might already exists, or might need to follow backend requirements)

demo/customer/created

Then we concatenate it to the namespace

company/customer/care/demo/customer/created

Result in dashboard:

Note:
Here we can enter multiple topics, e.g. for deleted customers and changed customers, etc
To save time, placeholder and patterns can be used

Note:
In Trial account: there are no rules

Note:
Just to prevent you from problems:
If you choose a topic with only one segment, then CAP cannot identify the topic. In that case you have to add prefix:
srv.on(‘ topic:customercreated’

4. Application Code

Now that we have collected information and defined queue and topic, we can continue with our application code
Remember: we have composed the topic string in our custom handler:

const topic = 'company/customer/care/demo/customer/created'

 

Now we can finally send the message:

srv.emit (topic, message) 

That’s all

Note;
Have you noticed?
The required code and configuration is REALLY VERY small and convenient…!
That’s CAP…

CAP takes care to use the underlying library to send the message to the Enterprise Messaging instance.
Please see the appendix for the full content of the custom handler
Only few more lines are required to implement the function

Deploy and Test

After writing this minimalistic application, we can deploy it to SAP Cloud Platform.
After successful deployment, we open the messaging dashboard.
There, we can see the “Number of Messages” which have reached the broker and have been put into the queue
Obviously, the number is zero

Now we want to send a message with our CAP application
Thus, we invoke the function import, as designed
In my example, the URL is as follows:

https://capsender.cfapps.eu10.hana.ondemand.com/sender/send()

Afterwards, we go back to the dashboard, press the refresh icon, and see that there’s one message in the queue:

The number increases every time we execute the function import

What can we do with the messages?

The message broker promises that messages are delivered asynchronously and that they don’t get lost.
As such, these messages will remain in the queue until we follow the next tutorial, where we learn how to write a CAP application which receives (consumes) messages

However… in the meantime… there’s a little tool which allows playing around with messaging:
The “Test” tab in the dashboard
So for now, let’s use it
In the “Test” tab, choose “Consume” action and choose our queue
Then press the button “Consume Message”
As a result, we can see the content of the message which we sent with CAP application
And we realize that the current time is more or less correct:

Summary

In this tutorial we’ve learned which steps are required in a CAP application in order to send messages to Enterprise Messaging in SAP Cloud Platform
Briefly:
package.json: add dependency and “requires” section for messaging
service.js: do a connect and an emit (to topic and with JSON content)

In this tutorial, we’ve manually configured the Enterprise Messaging client (create queue and subscription with matching topic name)
In the next tutorial, we’ll see that CAP can add some more convenience to our life

Links

Intro Blog: see here
Next Blog: create CAP app which receives messages from Enterprise Messaging
Extra Blog: local development
CAP managed messaging using keyword “event” in CDS model

CAP documentation : https://cap.cloud.sap
CAP messaging: https://cap.cloud.sap/docs/guides/consuming-services

SAP Help Portal: What is Enterprise Messaging
SAP Help Portal: Enterprise Messaging: Naming Syntax

Appendix 1: All Project Files

As mentioned above, our little sample app doesn’t contain a data model.
Only a service model file, to be placed in the “srv”-folder

manifest.yml

---
applications:
- name: capsender
  host: capsender
  path: .
  memory: 128M
  buildpacks:
    - nodejs_buildpack
  services:
    - msgcustcare

package.json

{
  "dependencies": {
    "@sap/cds": "^3",
    "express": "^4",
    "@sap/xb-msg-amqp-v100": "latest"
  },
  "cds": {
    "requires": {
      "messaging": {
        "kind": "enterprise-messaging"
      }
    }
  },
  "scripts": {
    "start": "npx cds run"
  }
}

sender-service.cds

service SenderService {

    function send() returns String;
}

sender-service.js

const cds = require ('@sap/cds')

module.exports = cds.service.impl ((srv) => {
 
  srv.on ('send', async(req)=>{
    const message = { 
      'myProp': 'Sending message. Current Time: ' + new Date().toLocaleString()
    }
  
    const topic = 'company/customer/care/demo/customer/created'
  
    srv.emit (topic, message)  

    return "Successfully sent event"
  })
})

Appendix 2: Legacy API

As mentioned above, the API for integrating CAP applications with Enterprise Messaging has changed from version 3.30 onward
In case you’re working with older version, see below for the relevant code changes
Note:
In old version, cds.connect.to() is required
and the returned instance is used for emit

package.json

{

    "cds": {
        "requires": {
            "myMessagingDef": {
                "kind": "enterprise-messaging"
            }
        }
    }
}

This connection has a name: in our example the name is “myMessagingDef”

As some of you know, I like to use silly names. Like that, whenever a silly name comes across, I know that it is not a predefined name, but instead, it was given by me. No framework would use a silly name as predefined name.
For you this means: whenever you come across a silly name, you can adapt it to a (silly) name of your choice. However, make sure to remember it, it will be used in the handler implementation

On the other side, this also means that the name “enterprise-messaging” which is configured as “kind”, MUST be a predefined name.

 

sender-service.js

const cds = require ('@sap/cds')

module.exports = cds.service.impl ((srv) => {

  const myMessaging = cds.connect.to("myMessagingDef")
 
  srv.on ('send', async(req)=>{
    const message = { 
      'myProp': 'Sending message. Current Time: ' + new Date().toLocaleString()
    }
  
    const topic = 'company/customer/care/demo/customer/created'
  
    myMessaging.emit (topic, message)  

    return "Successfully sent event"
  })
})

Note:
The name to be given in the “connect” statement (“myMessagingDef”) has to match the definition has been configured in package.json file

The result of the “connect” statement is an instance which can be used to interact with the messaging broker

1 Comment
You must be Logged on to comment or reply to a post.
  • Hi,

     

    I’m trying to run the service locally for development purposes.

    Do you know how I can config the service?

    I’m trying to fetch my env configuration via cf env my-service-env and then setting that in my default-env.json in the root directory, but it looks like the client cannot find the credentials (it says “[ERROR] No messaging credentials found: You need to bind your app to a message broker. “)

    Any ideas?

    Thank you

    Alex