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
cds.linked
in addition links all definitions…
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 definitionsentities
filters on entity definitionsservices
filters on service definitions
The methods each return an object of respective definitions. Object destructuring 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
returningtrue
orfalse
- 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 functiondefs
— 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 versionannos: 'off'
— don’t add annotationsannos: 'only'
— only render an annotation.xmlannos:
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
cds.compile.to.openapi (csn) → openapi
You can now convert CDS models to the OpenAPI Specification, a widely adopted API description standard.
For example, this is how you convert all services in srv/
and store the API files in the docs/
folder:
cds.compile.to.openapi (csn)
# command line
cds compile srv --service all -o docs --to openapi
Then, import the files in the online Swagger editor or serve the Swagger UI in your Node.js app.
With the --openapi:diagram
parameter, you can also include a yuml ER diagram of the service entities in the Open API file:
Swagger UI with an ER diagram for the Bookshop service entities
Supported Annotations
Find here the reference of supported annotations.