Skip to Content
Technical Articles
Author's profile photo Carlos Roggan

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

Troubleshooting

Make sure that you have created queue and subscription in the dashboard
Make sure that in your code the topic matches exactly the topic in the dashboard
Make sure you’re using the current CAP API and declared the code accordingly
If you have headache because messages aren’t arriving in Messaging Queue, you should check this guide

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

Assigned Tags

      16 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Alessandro Vitali
      Alessandro Vitali

      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

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hi @alessandro.vitali.bit

      Have you checked this blog dedicated to local development?
      Not sure if that error message is thrown if you haven't re-formatted the env configuration.
      Have you bound your app to messaging instance? Otherwise the credentials wouldn't be in the env
      Does my-service-env represent your app-name?

      These are my ideas for the moment;-)

      Cheers and good luck,

      Carlos

      Author's profile photo Vishali V
      Vishali V

      Hi Carlos,
      Thanks for the blog series. I was looking forward to this topic since a long time. It was very helpful in understanding certain key concepts of Enterprise Messaging.

      I have followed your blog series and tried implementing it. While trying to implement it, the CAP sender application is working fine, but in the Enterprise messaging service dashboard, the connections is 0 and messages count is also 0 and not increasing. What could be the point that I am missing out.

      I am using a trial account.

      Thanks for your time.

      Vishali

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hi Vishali V , I'm glad that these blogs are helpful for you.
      Nevertheless, I hope you'll see the messages arriving in dashboard.
      One possible reason could be that you're using wrong API, I mean legacy code with new library. So make sure, as described, to check which version of CAP you're using and if the implementation matches the description
      Have you checked if your code in CAP is invoked at all? There can always be a typo, such that the handler doesn't get invoked
      Also, a possible reason, some typo wrt the "topic", which must be exactly the same in code and in dashboard
      Another possibility, the message which you're sending must be valid json
      Last option would be to debug the CAP code ( there's a messaging libraryin @sap module)
      I had such problems frequently, and I guess you hate them like me...;-)

      Cheers and Wish you good luck!!
      Carlos

      Author's profile photo Vishali V
      Vishali V

      Hi Carlos,

      Thanks for your immediate reply. I figured out what was going wrong in this case. I had updated the package.json file present in the root folder of the CAP application with the extra piece of code.

      So instead, when I updated the same in the package.json file present in my SRV folder and rebuilt the application, I started seeing the message count increasing in the dashboard.

      Thanks for your time!

      Vishali

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hi Vishali V ,

      SUPER, I'm glad that you've seen those little messages;-)
      And thanks for updating the comment, that can always be useful for other users!
      Thanks!
      Carlos

      Author's profile photo Jason Scott
      Jason Scott

      Hi, Wondering if you can show what should be in the root package.json and also the package.json inside the srv folder?

       

      I am unable to get message sent regardless of whether I add:

      ```

      "messaging": {
      "kind": "enterprise-messaging"
      }
      ```
      To either or both package.json files.
      When I check the cloud foundry logs for the srv application I can see that its succesfully connected to enterprise messaging but I just cannot send any messages using:
      ```
      srv.emit ('ptcee/swatsafety/incident1/created', {
          'title':'Test event BEFORE',
          'description':'This is just a test event',
          'location':'Perth'
      })
      ```
      Author's profile photo Chunyang Xu
      Chunyang Xu

      Awesome series! Thanks a lot Carlos!

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Thanks Chunyang Xu !! I'm glad if it helps - and if everything works for you 😉
      Cheers, Carlos

      Author's profile photo Chunyang Xu
      Chunyang Xu

      Works all fine! I was even not aware EM is available in trial until I saw your great blogs 😛

      And I like the way you wrote it like a story 😀

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hey, cool, good to know that you liked it...;-) ... so I can continue like that 😉 Thanks and keep posting comments 😉

      Author's profile photo Deniz Cengiz
      Deniz Cengiz

      Hi Carlos, great tutorial series!

      I'm having some trouble getting this to work though. I am using a trial account, EM is running fine so far and when I set up a basic Node.js consumer and producer app everything worked.

      The issue I am having is as follows: When I run my CAP app locally and access http://127.0.0.1:4005/sender/send() I get the response message error 500 "No handler registered for SenderService.send".

      When I deploy the app to CF and then try to run the SenderService using <app-name>.cfapps.eu10.hana.ondemand.com/sender/send() I get the response message error 501 "The server does not support the functionality required to fulfill the request".

      Any idea how to tackle this issue?

      Thanks in advance!

      Dan

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hello Deniz Cengiz ,
      Thanks for the feedback ! 😉
      The issue you're having doesn't seem to be related to messaging at all, it seems that your CAP project is not set up properly.
      To run a CAP-based ODAta service, no custom implementation is required, no handler has to be registered. Only for functions/actions (or FunctionImport/ActionImport), a handler/custom code is required, as there cannot be any out-of-the-box handling by CAP-FWK
      So it looks like your custom handler is not found by the FWK. Maybe some typo in naming, wrong folder path, etc?
      Cheers,
      Carlos

      Author's profile photo Jason Scott
      Jason Scott

      Hi Carlos Roggan thanks for the great blog post series here... I have struggled for many many many - too many hours to get this working but have finally cracked it!

      It turns out with the latest version of CAP that you do indeed still need to perform the "connect" operation:

      const messaging = await cds.connect.to('messaging')

        and then to emit an event I needed to do this:

      messaging.tx(req).emit('<topic_name>', {
          'title':'blah',
          'description':'blah',
          'location':'blah'
      })

      If I simply do

      srv.emit (topic, message)

      as you are showing above - I get no errors anywhere - but no message ever gets into the enterprise messaging service.

      So I'm not sure what's going on - maybe the CAP team have reverted to the old ways of messaging.  😉

       

      Shout out to HandsOnSAPDev Adams / DJ Adams for the awesome HandsOnSAPDev video series where I found the answer!

      It would be awesome if this info could be included in the otherwise-wonderful CAP documentation at https://cap.cloud.sap/docs/ which currently just says "more to come...".

       

      Author's profile photo DJ Adams
      DJ Adams

      Cheers for the shout out, Jason Scott !

      In case folks are wondering, there's a series "Diving into messaging on SAP Cloud Platform" - share & enjoy!

      Author's profile photo Maximiliano Colman
      Maximiliano Colman

      Hello, is there any way to consume text and not json messages from the queue?, it seems that is always expecting json messages the cap amqp client.