Search

Node.js API Reference

as provided through @sap/cds

This is the reference documentation for the Node.js APIs, both for design-time as well as runtime usage. All APIs can be accessed through the singleton cds facade object obtained through:

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

These APIs are the center of all cds operations, for example, the cds command line interface is just a thin wrapper around these APIs.

Conventions

We use the following conventions to describe method signatures:

cds.connect.to (service, options?) service
cds.serve (service) … service
.from (model)
.at (path)

  • param? — appended question marks denote optional parameters
  • result — solid line arrows: returns the given result
  • result — dashed arrows: returns a Promise resolving to the given result
  • ... — denotes a fluent API, eventually returning/resolving to given result
  • — denotes subsequent methods to add options in a fluent API

Try clicking on the arguments and return types to visit specific documentation.

Index

cds.connect service

Use cds.connect to connect to local or external services; the latter include databases and are configured upfront in order to connect to them.

By default, bootstrapping is done by the framework, including connecting to the primary data source. You need cds.connect only if you want to control the bootstrapping yourself, for example, in a custom express server.js.

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.

Common Usage:

const srv = await cds.connect.to ('some-service')
const { Books } = srv.entities
srv.run (SELECT.from(Books))

Arguments:

If both, service and options are given, the ad-hoc options are merged with the configured ones (overriding the configured values on same properties). If service is omitted, this is an ad-hoc connection only.

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.

Alternative Usages:

Connect to a required service (→ standard for productive code)

cds.connect.to ('db')

Ad-hoc connection (→ only for tests)

cds.connect.to ({ kind:'sqlite', credentials:{database:'my.db'} })

Shortcut for ad-hoc connections

cds.connect.to ('sqlite:my.db')

cds.connect (options?) cds

Connects to the primary data source. Create this connection once in your code and then use the service requests methods provided by the singleton cds instance, such as cds.transaction, cds.run, etc.

cds.connect is actually a shortcut to cds.connect.to plus wiring the connected service as the primary data source.

Common Usage:

cds.connect() //... to primary data source, that is, 'db'
const { Books } = cds.entities
cds.run (SELECT.from(Books))

Arguments:

  • options can be a data source name, a URI string or a connect options object as documented in cds.connect.to. You can omit it completely to connect to the default data source, that is the one configured under cds.requires.db.

Returns cds itself

Acts as a proxy to the connected primary data source, supporting all the service requests methods. The following two code samples are equivalent:

await cds.connect()
const { Books } = cds.entities ('my.bookshop')
const books = await cds.run (SELECT.from(Books))
const db = await cds.connect.to ('db')
const { Books } = db.entities ('my.bookshop')
const books = await db.run (SELECT.from(Books))

Connect Options

The following options are supported, both in service configurations as well as the adhoc options parameter to cds.connect:

.model : string | csn

Specifies the service or data model to use for this connection, either as an already parsed csn or as a filename of a model, which is then loaded with cds.load.

.credentials : { … }

Specific options, which are passed to native driver libs, for example node-sqlite3

  • journalMode - SQLite PRAGMA journal mode as a string.

.multiTenant : boolean

Specifies if multitenancy is enabled. Defaults to false.

Configuring Required Services

When connecting to services using cds.connect.to, the provided service names are used to look up details and credentials from respective configurations, for example in your package.json:

  "cds": {
    "requires": {
      "db": {
        "model": "db/bookshop",
        "kind": "sqlite",
        "credentials": {
          "database": "db/bookshop.db"
        }
      },
      "cat-service": {
        "kind": "odata",
        "model": "srv/external/cat-service",
        "credentials": {
          "url": "https://localhost:4004/cat"
        }
      }
    }
  }

Required service db is the designated primary data source, that is, the one being used when calling cds.connect without no arguments.

The content per service configuration is the connect options as accepted and documented for cds.connect. Content of the credentials sections is specific to the respective backend/driver.

cds.serve… service(s)

Use cds.serve in to construct service providers from the service definitions in corresponding CDS models. Subsequently, you can add event handlers to the constructed providers.

Note: By default, bootstrapping of servers is handled by the framework automatically. So, you only need cds.serve if you want to control the bootstrapping yourself, for example, in a custom express server.js.

cds.serve (name) … service or { services }

Initiates a fluent API chain to construct service providers; use the methods documented below to add more options.

Common Usages:

const { CatalogService } = await cds.serve ('my-services')
const app = require('express')()
cds.serve('all') .in (app)

Arguments:

  • name specifies which service to construct a provider for; use all to construct providers for all definitions found in the models.
cds.serve('CatalogService')  //> serve a single service
cds.serve('all')             //> serve all services found

You may alternatively specify a string starting with './' or refer to a file name with a nonidentifer character in it, like '-' below, as a convenient shortcut to serve all services from that model:

cds.serve('./reviews-service')  //> is not an identifer thru '/'
cds.serve('reviews-service')    //> dito by '-', hence both act as:
cds.serve('all').from('./reviews-service')

The method returns a fluent API object, which is also a Promise resolving to either an object with 'all' constructed service providers, or to the single one created in case you specified a single service:

const { CatalogService, AdminService } = await cds.serve('all')
const ReviewsService = await cds.serve('ReviewsService')

Caching:

The constructed service providers are cached in cds.services, which (a) makes them accessible to cds.connect, as well as (b) allows us to extend already constructed services through subsequent invocation of cds.serve.

Common Usages and Defaults

Most commonly, you’d use cds.serve in a custom server.js file to add all the services to your express.js app as follows:

const app = require('express')()
cds.serve('all').in(app)
app.listen()

This uses these defaults for all options:

Option Description Default
cds.serve … which services to construct 'all' services
.from models to load definitions from './srv' folder
.in express app to mount to — none —
.to client protocol to serve to 'fiori'
.at endpoint path to serve at @path or .name
.with implementation function @impl or ._source.js

Alternatively you can construct services individually, also from other models, and also mount them yourself, as document in the subsequent sections on individual fluent API options.

.from (model)

Allows to determine the CDS models to fetch service definitions from, which can be specified as one of:

  • a filename of a single model, which get’s loaded and parsed with cds.load
  • a name of a folder containing several models, also loaded with cds.load
  • the string 'all' as a shortcut for all models in the './srv' folder
  • an already parsed model in CSN format

The latter allows you to cds.load or dynamically construct models yourself and pass in the CSN models, as in this example:

const csn = await cds.load('my-services.cds')
cds.serve('all').from(csn)...

If omitted, './srv' is used as default.

.to (protocol)

Allows to specify the protocol through which to expose the service. Currently supported values are:

  • 'rest' plain http rest protocol without any OData-specific extensions
  • 'odata' standard OData rest protocol without any Fiori-specific extensions
  • 'fiori' OData protocol with all Fiori-specific extensions like Draft enabled

If omitted, 'fiori' is used as default.

.at (path)

Allows to programmatically specify the mount point for the service.

Note that this is only possible when constructing single services:

cds.serve('CatalogService').at('/cat')
cds.serve('all').at('/cat') //> error

If omitted, the mount point is determined from annotation @path, if present, or from the service’s lowercase name, excluding trailing Service.

service MyService @(path:'/cat'){...}  //> served at: /cat
service CatalogService {...}           //> served at: /catalog

.in (express app)

Adds all service providers as routers to the given express app.

const app = require('express')()
cds.serve('all').in(app)
app.listen()

.with (impl function)

Allows to specify a function that adds event handlers to the service provider, either as a function or as a string referring to a separate node module containing the function.

cds.serve('./srv/cat-service.cds') .with ('./srv/cat-service.js')
cds.serve('./srv/cat-service') .with (srv => {
  srv.on ('READ','Books', (req) => req.reply([...]))
})

learn more about adding event handlers

Note that this is only possible when constructing single services:

cds.serve('CatalogService') .with (srv=>{...})
cds.serve('all') .with (srv=>{...})  //> error

If omitted, an implementation is resolved from annotation @impl, if present, or from a .js file with the same basename than the CDS model, for example:

service MyService @(impl:'cat-service.js'){...}
srv/cat-service.cds  #> CDS model with service definition
srv/cat-service.js   #> service implementation used by default

cds.services { services }

All constructed or connected services are registered and cached in cds.services. Each service instance provides access to metadata through the properties and methods listed below.

cds.services { services }

A dictionary and cache of all services constructed through cds.serve as well as connected to by cds.connect so far.

srv.entities, types (namespace) {defs}

These methods provide convenient access to the linked definitions of all entities and types managed by this service.

const db = await cds.connect.to('db')
const { Books, Authors } = db.entities('my.booskhop')

These methods are actually shortcuts to their counterparts provided by linked models, with the default namespace being the service definition’s name.

srv.definition def

The linked service definition contained in the model which served as the blueprint for the given service instance.

srv.model csn

The linked model from which this service’s definition was loaded.

srv.name string

The service’s name, i.e.:

cds.serve ('CatalogService')   //> .name = 'CatalogService'
cds.connect.to ('audit-log')   //> .name = 'audit-log'
cds.connect.to ('db')          //> .name = 'db'

srv.options { … }

The effective connect options used for connecting to this service.

srv.on/before/after (event)

Register event handlers with provided or connected services to add custom logic to serve operations or react to events emitted by these services.

Where to register event handlers?

You can register event handlers wherever you have access to a provided or connected service instance, which includes these options:

  1. In code using the outcomes of cds.serve or cds.connect:
    const {CatalogService} = await cds.serve('./cat-service')
    CatalogService.on ('READ','Books', req => {...})
    
    const db = await cds.connect('db')
    db.on ('INSERT','Orders', req => {...})
    
  2. In an inline function passed to cds.serve.with():
    cds.serve('./cat-service') .with (srv=>{
     srv.on ('READ','Books', req => {...})
    })
    
  3. In an inline function passed to srv.impl():
    const {CatalogService} = await cds.serve('./cat-service')
    CatalogService.impl (srv=>{
     srv.on ('READ','Books', req => {...})
    })
    
  4. In a .js file specified thru a string passed to cds.serve.with():
    cds.serve('./cat-service') .with ('./cat-service.js')
    
  5. In a .js file specified thru annotation @impl on a service definition:
    service CatalogService @(impl:'cat-service.js') {...}
    
  6. In a .js file placed next to the .cds file containing the service definitions:
    // cat-service.cds
    service CatalogService {...}
    
    // cat-service.js
    module.exports = (srv)=>{
     srv.on ('READ','Books', req => {...})
    }
    

srv.on (event, entity?, handler) this

Handlers registered with this method are run in sequence, with each handler being able to terminate the sequence (cf. Outcomes below). This termination capability in combination with registering handlers using srv.impl, lets you register custom handlers to run instead of the generic ones.

Arguments:

  • event — name of a single event or an array of multiple such
  • entity — name of an exposed entity or an array of multiple such
  • handler(req, next) — the handler function

learn more about the req argument in handler functions

Outcomes:

The request can be handled in one of the following ways:

  1. Reject the request via req.reject, throw, or returning a rejecting Promise.
    srv.on('READ','Books', (req)=> req.reject(409,'...'))
    srv.on('READ','Books', (req)=> Promise.reject(...))
    srv.on('READ','Books', (req)=> { throw ... })
    
  2. Call req.reply synchronously or asynchronously → ends the handling
    srv.on('READ','Books', (req)=> req.reply('Hello World!'))
    srv.on('READ','Books', (req)=> cds.run(...).then(req.reply))
    
  3. Return results or a Promise resolving to some → framework does req.reply
    srv.on('READ','Books', ()=> cds.run(SELECT.from(Books)))
    srv.on('READ','Books', ()=> "Hello World!")
    
  4. Return a cds.ql object → framework does cds.run and req.reply
    srv.on('READ','Books', ()=> SELECT.from(Books))
    
  5. Return nothing/falsy → passes control to next / default handlers
    srv.on('READ','Books', ()=> console.log('like before handler'))
    
  6. Use next as in express.js to explicitly delegate to default handlers
    srv.on('READ','Books', (req,next)=>{
      if (...) return SELECT.from(Books) //> ... handle req my own
      else return next()  //> delegate to next/default handlers
    })
    

Shortcuts:

As a yet more convenient shortcut to 4. you can just pass in the cds.ql object instead of a handler function; so, overall, these examples are equivalent:

srv.on('READ','Books', (req)=> {
  const tx = cds.transaction (req)
  return tx.run(SELECT.from(Books)) .then (req.reply)
)
srv.on('READ','Books', (req)=> {
  const tx = cds.transaction (req)
  return tx.run(SELECT.from(Books)) // framework will reply
)
srv.on('READ','Books', ()=> SELECT.from(Books))
srv.on('READ','Books', SELECT.from(Books))

srv.before (event, entity?, handler) this

Registers a handler to run before the ones registered with .on(), that is, before the generic handlers. Commonly used to add custom input validations.

Arguments are the same as in srv.on, except that the handler only has the request argument (that is, handler(req)).

Examples:

this.before ('CREATE','Order', (req)=>{
  const order = req.data
  if (order.amount > 11)  throw 'Order amount must not exceed 11'
})

learn more about the req argument in handler functions

You can as well trigger additional operations in before handlers:

srv.before ('CREATE','Order', (req)=>{
  const order = req.data
  return UPDATE(Books).set ('stock -=', order.amount)
})

Note, that this example would fail assumed you have a constraint on book stocks to not become negative, so the UPDATE also acts as a validation.

Note:

  • all before handlers are executed in order of their registration
  • always return a promise if your handlers trigger asynchronous tasks
  • all promises returned are collected in a Promise.all and
  • the next phase running the .on handlers starts when all promises resolved

Hence: You can terminate requests early by throwing exceptions or via req.reject in the handlers’ synchronous parts. Asynchronous parts however run in parallel, so you can’t react on side-effects between .before handlers.

srv.after (event, entity?, handler) this

Registers a handler to run after the generic handler. To be more precise: It runs on the results returned by the generic handler (which is asynchronous to the incoming handlers). Use it to modify the response.

Arguments are the same as in srv.on, except that the handler has the result set array as first and the request as second argument (that is, handler(data, req)).

Example:

this.after('READ','Books', (books)=>{
  for (let each of books) each.stock > 111 && each.discount='11%'
})

Name the param each as convenience shortcut for a per-row handler:

this.after('READ','Books', (each)=>{
  each.stock > 111 && each.discount='11%'
})

Note: This is only meant for synchronous code due to performance.

Use the secondary param req in case you need to reflect on the inbound request:

this.after('READ','Books', (books,req)=>{
  if (req.data.ID) ...
})

Only synchronous modifications are allowed. That is, replacements and asynchronous modifications like in the following two examples aren’t possible:

this.after('READ','Books', (books)=>{
  return \<something else...\>
})
this.after('READ','Books', async (each)=>{
  const orders = await SELECT.from('Orders').where({book_ID: each.ID})
  orders.length > 100 && each.description = `Bestseller! ${each.description}`
})

Reasoning: Multiple after handlers can be added by different involved packages, including extension packages, and each of which would expect the same input. If one would exchange the results, the chain would break.

In order to replace/ asynchronously modify the result, use on handlers as follows:

this.on('READ','Books', async (req,next)=>{
  const books = await next()
  return \<something else...\>
})

In case the result is a primitive, for example, a string, then also synchronous modifications must be done in an on handler as the change would stay local to the after handler (pass by value).

srv.reject (event, entity?) this

Registers a generic handler that automatically rejects incoming request with a standard error message. You can specify multiple events and entities.

Arguments:

  • event — name of a single event or an array of multiple such
  • entity — name of an exposed entity or an array of multiple such

Examples:

this.reject ('READ', 'Orders')
this.reject (['CREATE','UPDATE','DELETE'], ['Books','Authors'])

srv.impl ((srv)=>{…})

Use this method to provide a service implementation function, which adds several event handlers to the service provider.

const {CatalogService} = cds.serve('all')
CatalogService .impl (srv => {
  srv.on ('READ','Books', (req) => req.reply([...]))
})

Handlers added in subsequent invocations are added and executed before the ones from prior invocations. This allows projects reusing a service to override handlers from the imported package. For example:

const CatalogService = cds.connect.to('CatalogService')
CatalogService .impl (srv => { // will be called prior to the ones above
  srv.on ('READ','Books', ...)
})

cds.service.impl ((srv)=>{…})

In case you placed an implementation function in a separate file, you may want to wrap your service implementation function by this method to gain code assists in tools, for example, IntelliSense in vscode, for example:

module.exports = cds.service.impl ((srv)=>{
  srv.on ('READ','Books', (req)=>{...})
})

The implementation simply returns the passed in function.

Multiple handlers for same events

Arbitrary numbers of handlers for the same events can be registered. Those registered with .on are executed in order of their registration, while those registered with .before or .after the execution order isn’t guaranteed; they might be executed in parallel.

cds.serve('cat-service') .with (function(){
  this.on('READ', Books, ()=>{/* called first */})
  this.on('READ', Books, ()=>{/* called second */})
})

Single handlers for multiple events

Omit the <entity> argument to register handlers for all entities. Or add handlers for all events as well as standard express.js middlewares with method .use:

cds.serve('cat-service') .with (function(){
  this.on ('READ', ()=> {/* handles read events on all entities */})
  this.on ('*', ()=> {/* handles all events on all entities */})
})

srv.run (query) results

Each provided or connected service supports the following client API to read and write data. The central methods are srv.transaction and srv.run, the others are largely convenient variants.

srv.transaction (context?) tx

Starts a new transaction or joins an already started one for the given context. Usually this is used in event handlers as follows:

srv.on ('READ','Books', (req)=>{
  const ds = await cds.connect.to('another-service')
  const { Books } = ds.entities
  const tx = ds.transaction(req)
  return tx.run (SELECT.from (Books))
})

The returned object supports the same methods to read or write data than the service it’s called on, that is, the ones listed below.

Arguments:

  • context can be an arbitrary object to which the transaction is assigned. Most frequently it’s the req argument in an event handler. If omitted, a new transaction will be started.

Analysis:

Use this method whenever you call another service from the event handlers of your service. It ensures…

  • appropriate client credential flows and tenant isolation
  • propagation of requisite request context properties, such as the current user’s preferred language

For database services this method in addition starts atomic transactions, which are automatically committed or rolled back when an error occurs.

srv/tx.run (query, args?) results

This is the central function to run queries. It accepts CQN objects or plain query strings, for example, a SQL string in case of a connected SQL database and returns a Promise resolving to the queries’ results.

Common Usage:

const srv = await cds.connect.to ('SomeService') // > could be a local service, a remote one or a database
const { Books } = srv.entities
const books = await srv.run (SELECT.from(Books))

Arguments:

  • query is either a query object in cqn format or a native query string as understood by the service backend
  • args is an optional array or object of parameters bound to respective placeholders in the query

The following placeholders for binding parameters are supported:

  • :name — named parameters bound to respective entries in an args object
  • ? — positional parameters bound to entries of args in order of occurrence

Returns a Promise resolving to…

  • to a single number of affected rows in case of INSERT, UPDATE, DELETE.
  • an array of rows for SELECT queries

srv/tx.create (entity) .entries? (data) query

srv/tx.read (entity, key?) .where? (cond) query

srv/tx.update (entity, key?) .with? (data) query

srv/tx.delete (entity, key?) .where? (cond) query

srv/tx.insert (data) .into (entity) query

These methods construct and send requests in a CRUD method-call style instead of the Embedded QL style provided by cds.ql.

Common Usage:

await cds.read('Books').where({ID:111})

Arguments:

Essentially, each of this methods simply starts a fluent query construction using their cds.ql counterparts, which can be continued using the respective tail methods and sent to the service backend upon invocation of .then(). For example, think of the implementation of cds.read as follows:

read (entity) {
  const srv = this
  return Object.assign (SELECT.from(entity), {
    then (r,e) { return srv.run(this) .then(r,e) }
  }
}

Alternative Usages:

In effect, the following three variant to read books are equivalent:

const books1 = await db.run(SELECT.from(Books).where({ID:111}))
const books2 = await db.read(Books).where({ID:111})
const books3 = await db.get(Books).where({ID:111})
const books4 = await SELECT.from(Books).where({ID:111})

Method insert is just a SQL-reminiscent variant of create with the following being equivalent:

srv.insert(data) .into (entity)
srv.create(entity) .entries (data)

req context

Event handlers registered through service.on, .before, .after all accept an argument, which provides single point of API contact for accessing request input, reading/writing data, and sending responses.

req.method string

The HTTP method of the incoming request, such as…

  • POST
  • GET
  • PUT
  • PATCH
  • DELETE

req.target def

Refers to the current request’s target entity definition, if any; undefined for custom operations and events. The returned definition is a linked definition as reflected from the CSN model.

Learn more about linked models and definitions See Entity Definitions in the CSN reference

req.query cqn

Captures the incoming request as a CQN query object. For example, an HTTP request like GET http://.../Books would be captured as follows:

req.query = {SELECT:{from:{ref:['Books']}}}

If bound custom operations req.query contains the query to the entity, on which the bound custom operation is called. For unbound custom operations req.query contains an empty object.

req.data {…}

Contains the request payload if CREATE and UPDATE requests.
Contains the keys of the entity if DELETE and READ requests on a single entity.

req.user {…}

Represents the currently logged in user. It contains the following properties:

  • req.user.id to access the current user’s unique ID, an arbitrary string. Defaults to anonymous if no user is provided.
  • req.user.locale to access the current user’s locale. Defaults to en if no locale is provided.
  • req.user.is(<role>) to check whether the user is assigned the given role.
  • req.user.has(<roles>) to check whether the user is assigned any of the given roles.
  • req.user.<attr> to access user-related attributes from JWT tokens.

req.user acts as a shortcut for req.user.id in queries/strings.

req.reply (data)

Serializes the data object in a protocol-specific way and sends the response.

req.info (code ?, msg, target?)

The messages are collected and put into header sap-messages as a stringified array.

The code argument is a number. String codes can be used through msg.code. The msg argument can be a plain string, or a plain or error object (msg instanceof Error), of which the mandatory properties code, message, and numericSeverity (defaulted to 2), as well as the optional properties longtextUrl and target are extracted if present and either a number or a string. msg.code and msg.target aren’t overwritten by the arguments code and target, respectively, in case both are specified.

Multiple calls to req.info in the same or different request handlers are allowed and the request continues without an error. That is, the individual messages are collected and replied to the caller in addition to the regular payload.

req.error (code?, msg, target?)

Returns an error to the client (cf. OData Error Response).

If the first argument is a number and a known status code, it’s used as the HTTP response code, otherwise the status code gets defaulted to 500. String codes can be used through msg.code. The msg argument can be a plain string, or a plain or error object (msg instanceof Error), of which the properties code, message, and target are extracted if present and either a number or a string. target or msg.target can be used to specify the target of the particular error (for example, the name of the affected property). msg.code and msg.target aren’t overwritten by the arguments code and target, respectively, in case both are specified.

Multiple calls to req.error in the same or different request handlers are allowed to collect validation errors. However, once req.error has been invoked during a stage (that is, before, on, and after), the subsequent stages are skipped. The individual error messages are collected and replied to the caller as the details of a generic error. If multiple errors with differing error codes, the HTTP response code gets defaulted to 500.

req.reject (code?, msg?, target?)

Rejects the request with the given HTTP response code and single message. No additional handlers will be executed once req.reject has been invoked. If called multiple times in the same handler, the last call wins (LIFO).

req.on (event, handler)

Use this method to register handlers, executed when the whole request is finished.

req.on('succeeded', () => {...}) // request succeeded
req.on('failed', () => {...}) // request failed
req.on('done', () => {...}) // request succeeded/failed

Note: Inside the handlers, you don’t have access to a database connection.

req._ {…}

Provides access to original inbound protocol-specific request objects. For events triggered by an HTTP request, it contains the original req and res objects as obtained from express.js.

def:

Building Queries

cds.ql… query

Use the fluent API functions documented below to construct queries, captured as CQN objects, which you execute later on using the srv.run methods of your connected services.

let foos = SELECT.from('Foo')
let bars = SELECT.from(foos).where({kind:'bar'})
cds.foreach (bars, console.log)

While the API resembes well-known SQL constructs, cds.ql isn’t locked in to SQL. It can be used to capture any kind of queries as well as applied to any kind of data sources, including NoSQL or OData.

SELECT .from (entity, columns?) …

SELECT.from ('Books').where ({author:11})
SELECT.from (Books,[
  'ID', 'title', 'author.name as name'
]).where ({stock:{'>':11}})

The first argument is either a string of an entity or an entity definition as obtained from a data source’s .entities method.

The optional second argument is the same as in .columns.

.where / having / groupBy / orderBy (expr) / limit (rows, offset)

These methods allow to fill in the respective standard SQL or CQL clauses. The arguments form expressions as explained in section Fluent Expression below.

SELECT (…columns) .from (entity) …

This is a syntactical sugar variant to SELECT.from(entity) .columns(...), closer to and hence a reminiscence to standard SQL. The arguments are the same as in .columns

SELECT .one (entity, columns?) …

This is a variant of SELECT.from that captures we’re interested in only the first entry. When executed, a single entry is returned instead of a result set, which means the following are equivalent:

const one = await SELECT.one(Authors).where({ID:111})
const all = await SELECT.from(Authors).where({ID:111})
const one = all[0]

SELECT .forUpdate ({wait?}) …

The argument of this method is optional. Currently it can include the following option:

  • wait is an integer value, that specifies when to return an error if a lock can’t be obtained on a record. If this option is not specified, then it results in db-specific default behaviour.

Records are locked, until the transaction has ended (commit or rollback).

const lock = await SELECT.from(Authors,ID).forUpdate()
const lock = await SELECT.from(Authors,ID).forUpdate({ wait:10 })




INSERT .into (entity) …

Constructs an INSERT statement. In addition to the standard INSERT.into(Foo) .columns(...) .values(...) it provides the variants .rows and .entries, which can automatically be mapped to prepared statements and hence processed more efficiently.

.columns (…)

Specifies the columns for which values are provided in a subsequently specified .rows or .values. Find examples below.

.values (…)

Specifies the values, which positionally match to specified .columns. Both, .columns and .values can alternatively wrapped into an array. The following examples are equivalent:

INSERT.into (Books) .columns (
  'ID', 'title', 'author_id', 'stock'
) .values (
  201, 'Wuthering Heights', 101, 12
)
INSERT.into (Books) .columns ([
  'ID', 'title', 'author_id', 'stock'
]) .values ([
  201, 'Wuthering Heights', 101, 12
])

.rows (…)

Allows inserting multiple rows with one statement where each row is an array of values with positional match to specified .columns, for example, as could be read from a CSV source.

INSERT.into (Books) .columns (
  'ID', 'title', 'author_id', 'stock'
) .rows (
  [ 201, 'Wuthering Heights', 101, 12 ],
  [ 251, 'The Raven', 150, 333 ],
  [ 252, 'Eleonora', 150, 234 ]
)

.entries (…)

Allows inserting multiple rows with one statement where each row is a record with named values, for example, as could be read from a JSON source.

INSERT.into (Books) .entries (
  { ID:201, title:'Wuthering Heights', author_id:101, stock:12 },
  { ID:251, title:'The Raven', author_id:150, stock:333 },
  { ID:271, title:'Catweazle', author_id:170, stock:222 }
)

.as (query)

Constructs a INSERT into SELECT statement.

INSERT.into('Bar') .as (SELECT.from('Foo'))

UPDATE (entity) …

UPDATE('Books') .set ({
  stock:{'-=':1},
  lastChanged:Date.now()
}) .where ({ID:01})

.set (…)

Specifies the key-value pairs, with keys being element names of the target entity and values being expressions

.where (expr)

Allows add standard SQL WHERE clauses. The arguments form expressions as explained in section Fluent Expression below.




DELETE .from (entity) …

DELETE.from('Books').where ({stock:{'<':1}})

.where (expr)

Allows add standard SQL WHERE clauses. The arguments form expressions as explained in section Fluent Expression below.




Fluent Expressions…

There are two primary variants to specify expressions, for example, in .where or .orderBy clauses.

cds.load (files) csn

Loads and parses a model from one or more files into a single effective model.

Arguments:

  • files is expected to be a single file or folder name, or an array of such
  • the file/folder names are resolved relative to the current working directory.
  • If no file extension is given, the following suffixes are appended in that order: .csn, .cds, /index.csn, /index.cds, /*.csn|cds.

Note: It is recommended to omit file suffixes to leverage automatic loading from pre-compiled CSN files instead of CDL sources.

Returns a Promise resolving to parsed model in CSN format, which…

  • is merged from all passed in sources, plus all imported ones,
  • has all extensions applied,
  • and all queries and derived signatures inferred

Usages:

// load a model from a single source
const csn = await cds.load('my-model')
// load a a model from several sources
const csn = await cds.load(['db','srv'])
// load relative to current working directory
cds.load('relative-to-cwd')
cds.load('./relative-to-cwd')
// load relative to current node module
cds.load(__dirname+'/relative-to-this-module')

cds.parse (cdl) csn

This is both, a function acting as a shortcut to cds.parse.cdl as well as the root for set of individual functions to parse whole CDL models, as well as individual CQL queries and expressions. Use it as follows:

cds.parse (`entity Foo{}`)  //= shortcut to:
cds.parse.cdl (`entity Foo{}`)
cds.parse.cql (`SELECT * from Foo`)
e = cds.parse.expr (`foo.bar > 9`)
x = cds.parse.xpr (`foo.bar > 9`)
r = cds.parse.ref (`foo.bar`)

The last three lines would return these CXN expressions respectively:

e = {xpr:[ {ref:['foo', 'bar']}, '>', {val:9} ] }
x = [ {ref:['foo', 'bar']}, '>', {val:9} ]
r = ['foo', 'bar']


The individual methods are:

cds.parse.cdl (cdl) csn

Parses a source string in CDL syntax and returns it as a parsed model according to the CSN spec.

cds.parse.cql (cql) cqn

Parses a source string in CQL syntax and returns it as a parsed query according to the CQN spec.

cds.parse.expr (cql expr) expr

Parses a source string in CQL expression syntax and returns it as a parsed expression according to the CQN Expressions spec.

cds.parse.xpr (cql expr) expr.xpr

Convenience shortcut to cds.parse.expr(x).xpr

cds.parse.ref (cql expr) expr.ref

Convenience shortcut to cds.parse.expr(x).ref

cds.reflect (csn) csn

Use these methods to reflect given models.

cds.reflect / linked (csn) ReflectedModel

Both methods, cds.reflect and cds.linked construct and return decorators for given parsed models, which provide the reflection methods documented below.

Returned instances are cached, so subsequent calls to cds.reflect with the same parsed model return the same cached instance.

let model = await cds.load ('some-cds-model')
let reflected = cds.reflect (model)       //> result is cached
let reflected2 = cds.reflect (model)      //> === reflected
let reflected3 = cds.reflect (reflected)  //> === reflected

Linking means protoype-chaining all definitions, including element definitions, to their base definitions up to one of cds.builtin.types. This in turn allows to use all the methods as documented below on all definitions and elements thereof.

↳ m.exports / entities / services (namespace) {defs}

Provide convenient access to a model’s definitions within a given namespace.

Typical usages:

let m = cds.reflect (csn)
let [ CatalogService ] = m.services
let { Books, Authors } = m.entities ('my.bookshop')

If no namespace is specified, the model’s declared namespace is used, if any.

  • exports provides access to all kinds of definitions
  • entities filters on entity definitions
  • services filters on service definitions

The methods each return an object of respective definitions. Object descruturing operators allow to easily access single definitions by name as shown above.

↳ m.each* (x, defs?) Iterator<defs>

Fetches definitions matching the given filter, returning an iterator on them.

let m = cds.reflect (csn)
for (let d of m.each('entity')) {
  console.log (d.kind, d.name)
}

The first argument x speficies a filter to match definitions, wich can be one of…

  • a function returning true or false
  • a string referring to a kind of definition

Derived kinds are supported, for example, m.each('struct') matches structs as well as entities; kind 'any' matches all.

The second optional argument defs allows to specify the definitions to fetch in, defaults to this.definitions.

↳ m.all (x, defs) [defs]

Convenience shortcut to [... model.each()], for example, the following are equivalent:

m.all('entity')        //> using shortcut
[...m.each('entity')]  //> using spread operator

↳ m.find (x, defs) def

Convenience shortcut to fetch definitions matching the given filter, returning the first match, if any. For example:

let service = m.find('service')

The implementation uses to .each() as follows:

for (let any of m.each('service'))  return any

↳ m.foreach / forall (x, visitor, defs)

Calls the visitor for each definition matching the given filter. foreach iterates through the passed in defs only, forall in addition walks through all nested element definitions hierarchically.

  • x — the filter to match definitions → see .each(x)
  • visitor — the callback function
  • defs — the definitions to fetch in, default: this.definitions

Examples:

// print the names of all services
let m = cds.reflect(csn)
m.foreach ('service', s => console.log(s.name))
// print the names of all Associations in Books element
let { Books } = m.entities()
m.foreach ('Association', a => console.log(a.name), Books.elements)
// print the names of all Associations in the model
m.forall ('Association', a => console.log(a.name))
// print hierarchy of all definitions recursively
let m = cds.linked(csn)
m.forall (d => {
  let s=''; for (let p=d.parent; p; p=p.parent)  s += '  - '
  console.log (s, d.kind, d.name)
})

cds.builtin.types

The dictionary of built-in root definitions. These are linked definitions which serve as prototypes of derived definitions in linked models.

let model = cds.parse (`
   entity Books { author: Association to Authors; }
   entity Authors { key ID: UUID; }
`)
let m = cds.linked(model)
let { Books, Authors } = m.entities
let isEntity = Books instanceof cds.entity
let keys = Books.keys
let { author } = Books.elements
if (author.is2many) ...

See also: cds.builtin.classes

↳ any instanceof built-in class true|false

You can use JavaScript’s standard instanceof in combination with the built-in classes to check a linked definition’s type:

let m = cds.linked(csn)
let { Foo } = m.entities
if (Foo instanceof cds.entity)  console.log ("it's an entity")

↳ any.name / kind string

  • name is the reflected definition’s qualified name.
  • kind is the reflected definition’s kind

Kind is one of context, service, entity, type, element or annotation as documented in the CSN specification.

↳ any.parent def

Refers to the parent definition of elements. Is undefined for top-level definitions.

↳ entity.keys {defs}

Returns an object with an entity definition’s declared primary keys by name. The returned object adheres to the specification of CSN Definitions.

↳ assoc._target def

Refers to the association’s resolved target definition.

↳ assoc.is2one / is2many true|false

Convenient shortcut to check whether an association definition is to-one or to-many. See also the specification of CSN Associations

cds.builtin.classes

This property gives you access to the roots of cds’s type system: (indentation indicates inheritance)

any
  type
    scalar
      boolean
      number
      date
      string
    struct
      entity
    Association
      Composition
  context
    service

A few prominent ones of the above classes are available through these shortcuts:

  • cds.Association
  • cds.Composition
  • cds.context
  • cds.service
  • cds.entity
  • cds.struct

You can also add mixin behavior to selected classes:

cds.entity .mixin ({
  hello(){ console.log ("I'm an entity named", this.name) }
})
cds.Association .mixin ({
  hello(){ console.log ("I'm an Association to", this._target.name) }
})

cds.compile (csn) …

Entry point to a collection of different backend processors. Each of which expects a parsed model in CSN format as input and generates different output.

cds.compile (csn) .for (target)

cds.compile (csn) .to (target)

Fluent API variants for the methods below. For example, the following pairs are equivalent alternative ways to use cds.compile:

cds.compile(csn).for('odata')
cds.compile.for.odata(csn)
cds.compile(csn).to('yml')
cds.compile.to.yml(csn)

Fluent API variant to use the methods above. For example, the following pairs are equivalent alternative ways to use cds.compile:

cds.compile.for.odata (csn) csn

Applies unfold.all and in addition resolves certain specific shortcut annotations to their standard-OData counterparts.

cds.compile.to.json (csn) json

Renders the passed in csn model to a formatted JSON string.

cds.compile.to.yaml (csn) yaml

Renders the passed in csn model to a formatted YAML string.

cds.compile.to.sql (csn, options) SQL DDL

Generates SQL DDL statements for the given model. Returns an array with the generated statements unless called with option as=’str’, for example:

cds.compile.to.sql (csn, {as:'str'})

cds.compile.to.hana (csn) hdbcds

Reconstructs CDL source code for the given csn model specialized for deployment to HANA CDS. Returns a generator is returned that yields [ src, {name,suffix} ] for each resulting .hdbcds file. For example, use it as follows:

let all = cds.compile.to.hana (csn)
for (let [src,{name,suffix}] of all)  console.log (name+suffix,src)

cds.compile.to.edm (csn, options) edm

Compiles and returns an OData v4 EDM model object for the passed in model, which is expected to contain at least one service definition. If more than one service definitions are contained use {service:...} option parameter to …

  • either choose exactly one, for example, {service:'Catalog'}
  • choose to return EDM objects for all, i.e. {service:'all'}

In case of the latter, a generator is returned that yields [ edm, {name} ] for each service. For example, use it as follows:

// for one service
let edm = cds.compile.to.edm (csn, {service:'Catalog'})
console.log (edm)
// for all services
let all = cds.compile.to.edm (csn, {service:'all'})
for (let [edm,{name}] of all)  console.log (name,edm)

cds.compile.to.edmx (csn, options) edmx

Compiles and returns an OData EDMX model for the passed in model, which is expected to contain at least one service definition. If more than one service definitions use the same options as documented for cds.compile.to.edm above.

The following additional options are supported:

  • version: 'v2'|'v4' — determines the OData version
  • annos: 'off' — don’t add annotations
  • annos: 'only' — only render an annotation.xml
  • annos: undefined — include annotations in EDMX

cds.compile.to.annos (csn) edmx

Convenient shortcut to easily get an annotations.xml output for a given service model. Mainly provided for easy invocation from command line interface. For example, the following uses are equivalent:

cds.compile.to.edmx (csn, {annos:'only'})
cds.compile.to.annos (csn)
# command line
cds compile my-srv -2 annos
cds compile my-srv --to annos