Search

Model Processing in Node.js

The CDS compiler is implemented in Node.js. Find here the reference documentation for the respective APIs which can be used in CLI tools as well at runtime in CDS service implementations.

The cds Facade Object
All API functions, properties, and classes, documented in the following reference docs, are accessed through the global cds facade object, obtained through:
const cds = require('@sap/cds')

Learn more about the cds facade object in the Node.js reference docs

Index

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.
  • Any given folders are resolved to the files they directly contain.
  • If no file extension is given, the following suffixes are appended in that order: .csn, .cds, /index.csn, /index.cds, /*.csn|cds.

Note: It’s recommended to omit file suffixes to leverage automatic loading from precompiled 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 prototype-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 specifies a filter to match definitions, which 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, that means, {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