Connecting to Required Services
Services frequently consume other services, which could be local services served by the same process, or external services, for example consumed through OData.
The latter include database services. In all cases use cds.connect
to connect to such services, for example, from your:
Connecting to Required Services
cds.connect.to (name, options?) ⇢ service
Connects to a required service and returns a Promise resolving to a corresponding Service instance. Subsequent invocations with the same service name all return the same instance.
const srv = await cds.connect.to ('some-service')
const { Books } = srv.entities
srv.run (SELECT.from(Books))
Arguments:
name
is used to look up connect options from configured services.options
allows to provide ad-hoc options, overriding configured ones.
Caching:
Service instances are cached in cds.services
, thus subsequent connects with the same service name return the initially connected one. As services constructed by cds.serve
are registered with cds.services
as well, a connect finds and returns them as local service connections.
cds.connect.to (options) ⇢ service
Ad-hoc connection (→ only for tests):
cds.connect.to ({ kind:'sqlite', credentials:{database:'my.db'} })
cds.connect.to (‘<kind>:<url>’) ⇢ service
This is a shortcut for ad-hoc connections.
For example:
cds.connect.to ('sqlite:my.db')
is equivalent to:
cds.connect.to ({kind: 'sqlite', credentials:{database:'my.db'}})
Configuring Required Services
To configure required remote services in Node.js, simply add respective entries to the cds.requires
sections in your package.json
or in .cdsrc.json
(omitting the cds.
prefix). These configurations are constructed as follows:
"cds": {
"requires": {
"ReviewsService": {
"kind": "odata", "model": "@capire/reviews"
},
"OrdersService": {
"kind": "odata", "model": "@capire/orders"
},
}
}
Entries in this section tell the service loader to not serve that service as part of your application, but expects a service binding at runtime in order to connect to the external service provider. The options are as follows:
cds.requires.<srv>.impl
Service implementations are ultimately configured in cds.requires
like that:
"cds": { "requires": {
"some-service": { "impl": "some/node/module/path" },
"another-service": { "impl": "./local/module/path" }
}}
Given that configuration, a cds.connect.to('some-service')
would load the specific service implementation from some/node/module/path
.
Prefix the module path in impl
with ./
to refer to a file relative to your project root.
cds.requires.<srv>.kind
As service configurations inherit from each other along kind
chains, we can refer to default configurations shipped with @sap/cds
, as you commonly see that in our cap/samples, like so:
"cds": { "requires": {
"db": { "kind": "sqlite" },
"remote-service": { "kind": "odata" }
}}
This is backed by these default configurations:
"cds": { "requires": {
"sqlite": { "impl": "@sap/cds-runtime/.../sqlite/service" },
"odata": { "impl": "@sap/cds-runtime/.../odata/service" },
}}
Run
cds env get requires
to see all default configurations.
Runcds env get requires.db.impl
to see the impl used for your database.
Given that configuration, a cds.connect.to('db')
would load the generic service implementation.
cds.requires.<srv>.model
Specify (imported) models for remote services in this property. This allows the service runtime to reflect on the external API and add generic features. The value can be either a single string referring to a CDS model source, resolved as absolute node module, or relative to the project root, or an array of such.
"cds": { "requires": {
"remote-service": { "kind": "odata", "model":"some/imported/model" }
}}
Upon bootstrapping, all these required models will be loaded and compiled into the effective cds.model
as well.
Service Bindings
In addition to the static configuration of requires services documented above, additional information, such as urls, secrets, or passwords are required to actually send requests to remote endpoints. These are dynamically filled into property credentials
from process environments as explained below.
cds.requires.<srv>.credentials
All service binding information goes into this property. It’s filled in from process environment when starting server processes, managed by deployment environments. Service bindings provide the details about how to reach a required service at runtime, that is, providing requisite credentials, most prominently the target service’s url
.
For development purposes, you can pass them on the command line or add them to a .env
or default-env.json
file like so:
# .env file
cds.requires.remote-service.credentials = { "url":"http://...", ... }
❗ Never ever add secrets or passwords to package.json
or .cdsrc.json
!
General rule of thumb: .credentials
are always filled (and overridden) from process environment on process start.
One prominent exception of that, which you would frequently add to your package.json is the definition of a database file for persistent sqlite database during development:
"cds": { "requires": {
"db": {
"kind": "sql",
"[development]": {
"kind": "sqlite",
"credentials": {
"database": "db/bookshop.db"
}
}
}
}}
Basic Mechanism
At the end of the day, the CAP Node.js runtime expects to find the service bindings in the respective entries in cds.env.requires
:
-
Configured required services constitute end points for service bindings:
"cds": { "requires": { "ReviewsService": {...}, } }
-
These are made available to the runtime via
cds.env.requires
.const { ReviewsService } = cds.env.requires
-
Service Bindings essentially fill in
credentials
to these entries.const { ReviewsService } = cds.env.requires ReviewsService.credentials = { url: "http://localhost:4005/reviews" }
While you could do the latter in test suites, you would never ever provide credentials in a hard-coded way like that in productive code, of course. Instead, you’d use one of the options presented in the following sections.
Through process.env
Variables
You could pass credentials as process environment variables, for example in ad-hoc tests from the command line:
export cds_requires_ReviewsService_credentials_url=http://localhost:4005/reviews
export cds_requires_ReviewsService_db_credentials_database=sqlite.db
cds watch fiori
In .env
Files for Local Testing
Add environment variables to a local .env
file for repeated local tests:
cds.requires.ReviewsService.credentials = { "url": "http://localhost:4005/reviews" }
cds.requires.db.credentials.database = sqlite.db
Never check in or deploy such
.env
files!
Through VCAP_SERVICES
When deploying to Cloud Foundry, service bindings are provided in VCAP_SERVICES
process environment variables, which is JSON-stringified array containing credentials for multiple services. The entries are matched to the entries in cds.requires
as follows, in order of precedence:
- The service’s
name
is matched against thename
property ofVCAP_SERVICE
entries - The service’s
name
is matched against thebinding_name
property - The service’s
name
is matched against entries in thetags
array - The service’s
kind
is matched against entries in thetags
array - The service’s
kind
is matched against thelabel
property, e.g. ‘hana’ - The service’s
vcap.name
is matched against thename
property
All the config properties found in the first matched entry will be copied into the cds.env.requires.<i>\<srv\></i>.credentials
property.
Here are a few examples:
CAP config | VCAP_SERVICES |
|
|
|
|
|
|
In Target Cloud Platforms
Find information about how to do so in different environment under these links: