Providing & Consuming Services

Every active thing in CAP is a service. This applies to the application services you define in your project, as well as technical services delivered as parts of the CAP framework. This guide introduces the basics about defining, implementing, and publishing services, as well consuming other services.

Defining Services

All-In-One Service Definitions

In its most basic form, a service definition simply declares the types of entities it provides access to. For example:

service BookshopService {

  entity Books {
    key ID : UUID;
    title  : String;
    author : Association to Authors;

  entity Authors {
    key ID : UUID;
    name   : String;
    books  : Association to many Books on = $self;


We recommend plural forms for entities exposed by services.
See Naming Conventions for more details.

In fact, this simplistic service definition is all we need to start a full-fledged server, exposed through OData, or plain REST APIs, that serves CRUD requests automatically.

Services as Facades — Exposing Views on Underlying Entities

In contrast to all-in-one service definitions as shown before, services usually expose projections/views on domain model entities. In this way, services become facades to underlying data, exposing different aspects tailored to respective use cases.


We borrowed well-known concepts of view building from SQL, which in turn uses a reflexive query language to express projections and selections of entities based on underlying entities recursively.


using { sap.capire.bookshop as my } from '../db/schema';
service CatalogService @(path:'/browse') {
  @readonly entity Books as SELECT from my.Books {*, as author
  } excluding { createdBy, modifiedBy };

See this source in cap/samples.

Not bound to SQL data sources
Note that even though we borrowed the well-known, powerful, and proven language query language from SQL, this doesn’t mean that such projections are bound to SQL backends.
Key for Generic Providers
Capturing projections declaratively like this is a key prerequisite for providing generic providers, which automatically serve CRUD requests. Upon incoming requests, the generic handlers reflect the views in their services’ models to construct corresponding queries to connected data sources, which may be a database, but can also be a remote service.

CQL, the query language used in CAP, is based on standard SQL, hence, all the standard SQL clauses and language constructs, like where, order by, and so on, are available to express projections as shown in the previous example.

Auto-Redirected Associations

If two entities from an underlying model, A and B, are exposed in a service by projected entities Ax and Bx, all exposed associations from A to B and vice versa are redirected in the projection to Ax and Bx, respectively. This ensures that clients can navigate between projected entities as expected.

If there are two projections with the same ‘distance’ to the source, the algorithm to resolve the correct target may fail. For example, compiling the following model would result in this warning:

Target “C” is exposed in service “S” by multiple projections “S.Cx”, “S.Dx” - no implicit redirection CDS (redirected-implicitly-ambiguous).

using { cuid } from '@sap/cds/common';

entity A : cuid { b: Association to B; c: Association to C; }
entity B : cuid { a: Association to A; }
entity C : cuid { a: Association to A; }

service S {
  entity Ax as projection on A;
  entity Bx as projection on B;
  entity Cx as projection on C;
  entity Dx as projection on C;

Resolve this, by explicitly determining a redirection target in the projection of Ax. You can use redirected to and choose Cx for the association, to resolve that ambiguity as follows:

service S {
  entity Ax as projection on A { *, c : redirected to Cx };

Auto-Exposed Entities

Annotate entities with @cds.autoexpose to automatically include them in services containing entities with Association references to them.

For example, given the following entity definitions:

// schema.cds
using { sap.common.CodeList } from '@sap/cds/common';
entity Bar : CodeList { key code: Integer; }
entity Car @cds.autoexpose { key id: Integer; }

… a service definition like this:

using { Bar, Car } from './schema.cds';
service Zoo {
  entity Foo { //...
    bar : Association to Bar;
    car : Association to Car;

… would result in the equivalent of the following unfolded service definition:

service Zoo {
  entity Foo { //...
    bar : Association to Zoo.Bar;
    car : Association to Zoo.Car;
  entity Bar as projection on Bar;
  entity Car as projection on Car;

Learn more about CodeLists in @sap/cds/common.

Handling Events

About Events and Event Handlers

As introduced in About CAP, everything happening at runtime is in response to events, emphasizing the ubiquitous notion of events in CAP. This means that all service implementations take place in event handlers, as highlighted in the figure.

Events essentially comprise:

  • Synchronous Requests sent from UIs, frontends, or other services:
    • Common REST methods: POST, GET, PUT, PATCH, DELETE
    • Custom-defined actions and functions
  • Asynchronous Event Messages received through message brokers:

Event Handlers comprise:

So, lines are very much blurred, both with respect to synchronous and asynchronous protocols, as well as regarding generic handlers and custom handlers. In fact, all generic features are provided in event handlers, which are registered and implemented using the same APIs and libraries that you would use to implement your custom logic. Vice versa, you can implement your own generic handlers in the same way as we did in CAP runtimes.

Use Cases for Custom Handlers

Most standard tasks and use cases are covered by generic handlers, so the need to implement event handlers in CAP-based projects, and hence the amount of individual boilerplate coding, is greatly reduced and minified. The remaining cases that need custom handlers are:

  • Domain-specific programmatic Validations
  • Augmenting result sets, for example to add computed fields for frontends
  • Programmatic Authorization Enforcements
  • Triggering follow-up actions, for example calling other services or emitting outbound events in response to inbound events
  • And more… In general, all the things not (yet) covered by generic handlers

Assigning Custom Implementation Classes/Modules

At runtime, the CAP frameworks usually bootstrap and instantiate service provider instances automatically (→ see also options for project-specific bootstrapping in Node.js and Java). In order to register event handlers with a service provider instance, you first need to associate a respective class or module with a given service definition.

In Node.js, the easiest way to do so is by equally named .js files placed next to a service definition’s .cds file. Alternatively, you can add @impl: annotations to services, or provide implementation functions programmatically, for example:

service org.acme.Foo @(impl:'lib/foo.js') {...}
cds.serve('org.acme.Foo') .with (require('./lib/foo.js'))

In Java, you’d assign Event Handler Classes using Spring Dependency Injection with Java annotation @ServiceName as follows:

public class FooServiceImpl implements EventHandler {...}

Registering Event Handlers

Given assigned implementation classes/modules, you can register individual event handlers for each potential event, on different hooks of the event processing cycle, for example:

const cds = require('@sap/cds')
module.exports = function (){
  this.on ('submitOrder', (req)=>{...}) //> custom actions
  this.on ('CREATE',`Books`, (req)=>{...})
  this.before ('UPDATE',`*`, (req)=>{...})
  this.after ('READ',`Books`, (each)=>{...})

Learn more about adding event handlers in Node.js.

public class BookshopServiceImpl implements EventHandler {
  @On(event="submitOrder") public void onSubmitOrder (EventContext req) {...}
  @On(event="CREATE", entity="Books") public void onCreateBooks (EventContext req) {...}
  @Before(event="UPDATE", entity="*") public void onUpdate (EventContext req) {...}
  @After(event="READ", entity="Books") public void onReadBooks (EventContext req) {...}

Learn more about adding event handlers in Java.

In general, event handlers are registered as follows:
  • <hook:on|before|after> , <events> , <entities> → handler function
  • <hook:on|before|after> , <events> → handler function
Hooks: on | before | after
  • on handlers can run instead of the generic/default handlers
  • before handlers run before the on handlers
  • after handlers run after the on handlers, and get the result set as input
Entity-bound vs unbound events
As apparent from these two variants, <events> can be bound to <entities>, as with the common CRUD or REST methods, or unbound, as with custom-defined, service-level actions or functions. Entity-bound events are only triggered for requests directly targeting that entity.

For example, the following GET requests will only trigger the READ handler for MyEntity.

GET MyEntity
GET MyEntity?$expand=myNavigation

Consistently, a GET request like the following will only trigger the READ event handler for myNavigation’s target.

GET MyEntity(1)/myNavigation

Implementing Event Handlers…

Event handlers all get a uniform Request/Event Message context object as their primary argument, which, among others, provides access to the following:

  • the event name — that is, a CRUD method name, or a custom-defined one
  • the target entity, if any
  • the query in CQN format, for CRUD requests
  • the data payload
  • the user, if identified/authenticated
  • the tenant using your SaaS application, if enabled

Learn more about implementing event handlers in Node.js. Learn more about implementing event handlers in Java.

Consuming other Services

Frequently event handler implementations send requests to other services — mostly in the form of queries constructed using the respective incarnations of cds.ql libraries. Note that from CAP’s perspective, databases are also just services, consumed in your project, as shown in these examples:

const db = await'db), {Orders} = db.entities
this.on ('READ',`MyOrders`, async (req)=>{
  const tx = db.tx(req) //> ensure tenant isolation & transaction mgmt
  return (Orders) .where ({ user: })

Learn more about consuming services in Node.js.

import static bookshop.Bookshop_.MY_ORDERS;

PersistenceService db; // get access to the service

@On(event="READ", entity="MyOrders")
public Result onRead(EventContext req) {
  CqnSelect query = Select.from(MY_ORDERS).where(o -> o.user().eq(req.getUserInfo().getName()));

Learn more about consuming services in Java.

Emitting New Events

Event handlers may also emit new asynchronous events, as also showcased in cap/samples, here a simplified variant:

this.after (['CREATE','UPDATE','DELETE'], 'Reviews', async(_,req) => {
   const {subject,rating} =
   this.emit ('reviewed', { subject, rating })

Learn more about emitting events in Node.js.

Java: coming soon…

Using Model Reflection

As stated, you can also implement generic custom handlers. In this case you need to frequently reflect on related models, as shown in the following samples:

const m = this.model //> this = service instance
for (let each in this.entities) {
  const e = this.entities [each]
  console.log (`entity ${} {`)
  for (let a of m.each (cds.Association, /*in:*/ e.elements))
    if (a.is2many)  console.log (`   ${} : Association to many ${};`)
  console.log (`}`)

Learn more about model reflection in Node.js.

Java: coming soon…

Consuming Services

Every active thing in CAP is a service. This applies to the application services you define in your project, as well as technical services delivered as parts of the CAP framework. This guide introduces the basics about defining, implementing, and publishing services, as well consuming other services.

Each service has an associated data model or service definition as depicted in the following figure.

REST and OData

External services can be consumed using the Cloud SDK in combination with the destination service. To configure the destination service properly, please see Consuming the Destination Service.

🚫 Warning
Make sure, that destination configurations for deployment aren’t shared on any file shares (GitHub, etc.).

In the package.json or in the .cdsrc.json, the consumed service needs to be configured.

  • destination Name of the destination. (optional)
  • path relative path that will be appended to the resolved destination (optional)
  • requestTimeout ms until a request times out Default is 60,000 ms (optional)
  • model The CSN model of the external service
  • pool Connections to the external service are pooled to prevent DOS-Attacks. See the .pool section of the cds.connect documentation (optional)
  "cds": {
    "requires": {
      "externalService": {
        "kind": "rest/odata",
        "model": "path/to/model",
        "credentials": {
          "destination": "destinationName",
          "path": "/relativePath",
          "requestTimeout": 30000
        "pool": {
          "min": 1,
          "max": 10

If the destination is omitted, the runtime looks for a destination with the name of the configured data source. In the example, it looks for externalService.

For local usage, the runtime autowires the services, if the consumed service is started first. In the example, the externalService should be started before the service requiring it can be started.

If the external service isn’t served locally, the credentials to connect to the external service could be provided as part of the credentials section. Example:

  "destination": "destinationName",
  "url": "...",
  "username": "...",
  "password": "..",
  "requestTimeout": 30000

Sending Requests

There are multiple convenience methods for sending requests. For examples, please have a look at the API documentation of

❗ Warning
Not all methods of the fluent query API can be translated to an url of the configured service kind yet. So far, it’s possible to send create, update, and delete requests. In case of read, it’s only supported to read an entire collection or a single entity (query parameters, for example, filtering or ordering the result set isn’t supported).

Simple examples for reading data (works also for create, update, and delete):

// connect to external server
const srv ='my.external.service')
const { Authors } = srv.entities

// share request context with the external service
// inside a custom handler
const tx = srv.transaction(req)

// url string input
const response = await tx.get('/Authors?$select=name')

// CSN entity input and fluent query API
const response = await'ID', 1)

// CQN input from fluent query API
const cqn = SELECT.from(Authors)
const response = await

Uniform Consumption

With these fundamental concepts in place, we can implement additional features as derived concepts, offering the same uniform APIs to consumers of services. For example, consuming databases essentially looks the same as consuming local services served from the same server process, or remote services through OData or REST. Here’s a snippet in Node.js demonstrating this:

const srv = await'some-service')
// could be any of...
// - local service
// - remote service client
// - database client
const { Books } = srv.entities
let query = SELECT.from(Books).where({ID:111})
let books = await (query)
Databases as Services

From CAP perspective, databases are special services, and integrating a specific database technology is as easy as providing a service with a set of generic event handlers to translate inbound queries to native ones in calls to the underlying database technology (could be SQL as well as NoSQL).

Late-cut Micro Services

The uniform and transparent consumption APIs allow projects to flexibly change topologies - for example, starting with locally embedded services, which later on get externalized into separate micro services - without changing all consumers.

Protocols & APIs

Omitting Elements from APIs

Add annotation @cds.api.ignore to suppress unwanted entity fields (for example, foreign-key fields) in APIs exposed from this the CDS model, that is, OData or Open API. For example:

entity Books { ...
  author : Association to Authors;

OData (Open Data Protocol)

OData is an OASIS standard, which essentially enhances plain REST by standardized query options like $select, $expand, $filter, etc. CAP provides extensive support for OData.

Learn more in the OData Support guide.

Best Practices

Prefer Single-Purposed Services

We strongly recommend following to design your services to be tailored for single use cases. Services in CAP are cheap, so there’s no need to save on them.

DON’T: Single Services exposing all entities 1:1

The anti-pattern to that are single services exposing all underlying entities in your app in a 1:1 fashion. While that may save you some thoughts in the beginning, likelihood is high, it results in lots of headaches in the long run:

  • They open a huge entry door to your clients with only few restrictions
  • Individual use-cases aren’t reflected in your API design
  • You have to add numerous checks on a per-request basis…
  • Which have to reflect on the actual use cases in complex and expensive evaluations

DO: One Service per Use Case

For example, assumed that we got a domain model defining Books and Authors more or less as above, and adds Orders, we could define the following services:

using { my.domain as my } from './db/schema';
/** Serves end users browsing books and place orders */
service CatalogService {
  @readonly entity Books as select from my.Books {
    ID, title, as author
  @requires: 'authenticated-user'
  @insertonly entity Orders as projection on my.Orders;
/** Serves registered users managing their account and their orders */
service UsersService {
  @readonly entity Orders as projection on my.Orders
    where buyer = $user; // limit to own ones
  action cancelOrder ( ID:Orders.ID, reason:String );
/** Serves administrators managing everything */
@requires: 'authenticated-user'
service AdminService {
  entity Books   as projection on my.Books;
  entity Authors as projection on my.Authors;
  entity Orders  as projection on my.Orders;

These services serve different use cases and are tailored for each. Note, for example, that we intentionally don’t expose entity Authors to end users.

Show/Hide Beta Features