Search

Consuming Services

This guide is about consuming services in general, including Local Services, External Services, and Databases (currently specific to the Node.js runtime).

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

Content

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 cds.connect.to('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 srv.run (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.

Configuring Required Services

For HTTP-Based Consumption

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.

Caution: 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)
  • 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",
          "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 above example, it looks for externalService.

For local usage, the runtime autowires the services, if the consumed service is started first. In above 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
}

Connecting to Required Services

To connect to an external service, cds.connect.to should be used. Examples can be found in the linked API description.

Sending Requests

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

Caution: Please note, that 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 = cds.connect.to('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 tx.read(Authors).where('ID', 1)

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