Search

    Core Services APIs

    Class cds.Service is the base class for all provided and connected services. As such, and complemented by cds.connect and cds.Request, it provides the essential APIs for implementing and consuming services.

    Most frequently you would use these APIs from within custom implementations of your provided services. Here’s an example from cap/samples with references to used APIs:

    const cds = require('@sap/cds')
    
    class CatalogService extends cds.ApplicationService { async impl(){
      const db = await cds.connect.to ('db')
      const { Books } = db.entities ('sap.capire.bookshop')
    
      this.on ('submitOrder', async req => {
        const {book,quantity} = req.data
        let {stock} = await db.read (Books,book, b => b.stock)
        if (stock >= quantity) {
          await db.update (Books,book) .with ({ stock: stock -= quantity })
          await this.emit ('OrderedBook', { book, quantity, buyer:req.user.id })
          return req.reply ({ stock })
        }
        else return req.error (409,`${quantity} exceeds stock for book #${book}`)
      })
    
      this.after ('READ','Books', each => {
        if (each.stock > 111) each.title += ` -- 11% discount!`
      })
    }}
    
    module.exports = { CatalogService }
    

    APIs used:




    Subclassing cds.Service
    Using cds.connect
    Model Reflection

    Handler Registration
    Using req.data
    Querying
    Messaging
    Using req.reply
    Using req.error


    Handler Registration







    Content

    How to Implement Services

    Service implementations are essentially collections of event handlers registered with service instances to handle incoming requests and event messages. The sections below explain where and how to do so.

    Where to Implement Services?

    In sibling.js files next to .cds sources

    The easiest way to add service implementations is to simply place equally named .js files next to the .cds files containing the respective service definitions. In addition to direct siblings you can place them into relative subdirectories ./lib or ./handlers, allowing layouts like that:

    srv/
      # all in one
      foo-srv.cds
      foo-srv.js
      bar-srv.cds
      bar-srv.js
    
    srv/
      lib/
        foo-srv.js
        bar-srv.js
      foo-srv.cds
      bar-srv.cds
    
    srv/
      handlers/
        foo-srv.js
        bar-srv.js
      foo-srv.cds
      bar-srv.cds
    

    Service implementations basically look like that:

    // srv/lib/foo-srv.js
    const cds = require('@sap/cds')
    class FooService extends cds.Service {...}
    module.exports = FooService
    

    In files specified in @impl annotations

    Use the @impl annotation to specify alternative files to load implementations from:

    // srv/services.cds
    service FooService @(impl:'./lib/foo-service.js') {}
    service BarService @(impl:'./lib/bar-service.js') {}
    

    Learn more in configuration options on @impl.

    // srv/lib/foo-service.js
    const cds = require('@sap/cds')
    class FooService extends cds.Service {...}
    module.exports = FooService
    
    // srv/lib/bar-service.js
    const cds = require('@sap/cds')
    class BarService extends cds.Service {...}
    module.exports = BarService
    

    With multiple implementations in one file

    Assumed you declared multiple services in the same .cds file like that:

    // srv/services.cds
    service FooService {}
    service BarService {}
    

    Instead of providing separate implementation files specified by @impl annotations, you can also provide multiple implementations in a single sibling file like that:

    // srv/services.js
    const cds = require('@sap/cds')
    class FooService extends cds.Service {...}
    class BarService extends cds.Service {...}
    module.exports = { FooService, BarService }
    

    Note: The exported names are expected to match the names of the service definitions in your CDS model.

    How to Implement Services?

    As subclasses of cds.Service

    You can create subclasses of cds.Service, or subclasses thereof, from service implementation modules like so:

    // for example, in srv/cat-service.js
    const cds = require('@sap/cds')
    class CatalogService extends cds.ApplicationService {
      async init() {
        const { Books } = this.entities
        // register your event handlers...
        this.before ('CREATE', Books, req => {...})
        this.on ('UPDATE', Books, req => {...}) // overrides the default handler
        // ensure to call super.init()
        await super.init() 
      }
    }
    module.exports = CatalogService
    

    The init() method acts like a parameter-less constructor. Ensure to call await super.init() as in the previous example, to have the base class’s handlers added.

    As also shown in the previous example, you may register own handlers before the base class’s ones, to intercept requests before the default handlers snap in.

    You can also overload API methods of cds.Service, or subclasses thereof.

    As plain functions

    The simplest way to provide custom event handlers is to return a function that registers event handler with the instance of cds.Service as follows:

    module.exports = function(){ // `this` is the instance of cds.Service
      this.before (['CREATE','UPDATE'],'Books', (req)=>{...})
      this.on ('UPDATE','Books', (req)=>{...})
      this.after ('READ','Books', (books)=>{...})
    }
    

    Alternatively you can provide an arrow function like so:

    module.exports = (srv)=>{ // `srv` is the instance of cds.Service
      srv.on ('UPDATE','Books', (req)=>{...})
      // ...
    }
    

    With async/await

    Quite frequently you need to call async functions in your impl functions, just declare your impl function as async to use await inside:

    const cds = require('@sap/cds')
    module.exports = async function(){ 
      const SomeOtherService = await cds.connect.to('SomeOtherService')
      // ...
    }
    

    Wrapped with cds.service.impl

    Wrap the impl function into cds.service.impl(...), which simply returns the function but gives you code assists in tools like VSCode:

    const cds = require('@sap/cds')
    module.exports = cds.service.impl (function(){ ... })
    

    Configuration / Annotations

    Following are options used to control the construction of services, as either of:

    @impl  |  o.impl = class | instance | function | module name

    Use the impl option specify the implementation used by this service.

    Using the @impl Annotation

    Add an @impl annotation to your service definition referring to a file in your project to load the implementation from:

    // leading ./ -> relative to the .cds source
    service FooService @(impl:'./lib/foo-service') {...}
    // otherwise -> relative to project root
    service BarService @(impl:'src/lib/bar-service') {...}
    
    Using cds.requires.<service>.impl in Configurations

    For required services you can specify a custom implementation to use like so:

    "cds": { 
      "requires": {
        "reviews-service": { 
          "kind": "odata", 
          "impl": "srv/external/reviews-service" 
        },
      }
    }
    
    As ad-hoc options to cds.serve or cds.connect

    You can also specify ad-hoc options in calls to cds.serve or cds.connect like so:

    const ReviewsService = await cds.connect.to ('reviews-service', {
      impl: 'srv/external/reviews-service'
    })
    

    In this case, you can also specify an impl function, as subclass of cds.Service or an instance of such like so:

    const ReviewsService = await cds.connect.to ('reviews-service', {
      impl: 'srv/external/reviews-service'
    })
    

    @kind  |  o.kind = cds.requires.<kind>

    Use option kind to refer to an entry in the cds.requires config options, which in turn contains presets for the other options, in particular for impl.

    For Required Services

    In case of required services, you need to specify option kind in respective entries to the cds.requires config option like so:

    "cds": { 
      "requires": {
        "reviews-service": { "kind": "odata" },
        "db": { "kind": "sql" },
      }
    }
    

    The values usually refer to these pre-defined options:

    • sql, sqlite, hana → for database services
    • odata, rest → for external services
    • enterprise-messaging, file-based-messaging → for messaging services

    Run cds env get requires to see all default configurations.

    For Provided Services

    For services provided by your app, the default is kind = 'app-service', which is configured in built-in defaults like so:

    "cds": { 
      "requires": {
        "app-service": { "impl": "@sap/cds/lib/srv/ApplicationService.js" }
      }
    }
    

    Run cds env get requires.app-service to see the concrete default config.

    In effect, cds.serve constructs instances of cds.ApplicationService by default.

    Using Custom Kinds

    You can override this by adding a @kind annotation to your service definition:

    service FooService @(kind:'app-service') {...} // default
    service BarService @(kind:'bar-service') {...} // custom
    

    … backed by a corresponding entry to cds.requires config options:

    "cds": { 
      "requires": {
        "bar-service": { "impl": "srv/bar-service.js" },
      }
    }
    

    @path  |  o.path = string

    By default cds.serve determines the endpoints to serve individual services from the service definitions name, by 'slugifying' camel-case names, and removing ‘Service’` suffixes like that:

    SomeBookshopAdminServicesome-bookshop-admin

    @protocol  |  o.protocol = string

    @endpoints  |  o.endpoints = […{ path, protocol }]

    .path

    .protocol

    @credentials  |  o.credentials = { url, … }

    Specific options, passed to services. For example, in case of a SQLite database service these are driver-specific options node-sqlite3

    @model  |  o.model = csn

    The 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].

    Model Reflection API

    srv.name = string

    The service’s name, that means, the definition’s name for services constructed with cds.serve, or the name of required services as passed to cds.connect.

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

    srv.model = csn

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

    This is === cds.model by default, that is, unless you created services yourself with cds.serve, specifying alternative models to load and construct new services from.

    srv.definition def

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

    srv.namespace string

    srv.entities (namespace) {defs}

    srv.events (namespace) {defs}

    srv.operations (namespace) {defs}

    These methods provide convenient access to the linked definitions of all entities, events, as well as actions and functions provided by this service.

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

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

    Using Fully Qualified Names:

    Many service consumption methods, such as srv.run expect fully qualified names of entities or an entity definition. Using these reflection methods greatly simplifies code as it avoids repeating namespaces all over the place.

    For example, the fully qualified name of entity Books in cap/samples/bookshop is sap.capire.bookshop.Books. So, queries would have to be written like that using name strings:

    const books = await cds.read ('sap.capire.bookshop.Books')
    

    So, rather prefer that pattern:

    const { Books } = cds.entities
    const books = await cds.read (Books)
    

    Handler Registration API

    Register event handlers with instances of cds.Service to add custom logic to serve operations or react to events emitted by these services.

    srv.on (event, path?, 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.prepend], lets you register custom handlers to run instead of the generic ones. If you want to defer to the generic handlers, invoke next in your custom handler (see Handling Examples below).

    Arguments:

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

    A service handler registered by entity name can use a relative or fully qualified name, containing the service namespace. A database handler needs a fully qualified entity name.

    learn more about the req argument in handler functions.

    Registration Examples:

    // Direct request, for example, GET /Books
    srv.on('READ','Books', (req)=>{...})
    // Registration by fully-qualified name, for example, GET /Books
    srv.on('READ','serviceName.Books', (req)=>{...})
    // Navigation requests, for example, GET /Authors/201/authors
    srv.on('READ','Authors/books', (req)=>{ let [ID] = req.params; ...})
    // Registration by CSN, for example, GET /Books
    srv.on('READ', Books, (req)=>{...})
    // Calls to unbound actions/functions
    srv.on('cancelOrder', (req)=>{...})
    // Calls to bound actions/functions
    srv.on('cancel','Order', (req)=>{...})
    

    Handling Examples:

    Requests can be handled in one of the following ways:

    1. Call [req.reply] synchronously or asynchronously:
      srv.on('READ','Books', async (req)=> req.reply(await cds.tx(req).run(...))
      srv.on('READ','Books', (req)=> req.reply(...))
      
    2. Return results or a Promise resolving to some → framework calls [req.reply]:
      srv.on('READ','Books', (req)=> cds.tx(req).run(...))
      srv.on('READ','Books', (req)=> [ ... ])
      
    3. Return a cds.ql object → framework does cds.run and [req.reply]:
      srv.on('READ','Books', ()=> SELECT.from(Books))
      
    4. Call next as in express.js to delegate to handlers down the chain:
      srv.on('READ','Books', (req,next)=>{
        if (...) return SELECT.from(Books) //> ... handle req my own
        else return next()  //> delegate to next/default handlers
      })
      

      IMPORTANT: Ensure to properly add calls to next() in your promise chain, either using await or by passing through its return value as shown in the previous example.

    5. Reject the request through [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 ... })
      

    Shortcuts:

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

    const {Books} = srv.entities
    srv.on('READ','Books', (req)=> cds.tx(req).run(SELECT.from(Books))
    srv.on('READ','Books', ()=> SELECT.from(Books))
    srv.on('READ','Books', SELECT.from(Books))
    

    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.on (‘error’, (err, req) => {})

    Using the special event name error, you can register a custom error handler that is invoked whenever an error will be returned to the client. The handler receives the error object err and the respective request object req. Only synchroneous modifications of the error object are allowed.

    Examples:

    cds.serve('cat-service') .with (function(){
      this.on ('error', (err, req ) => {
        // modify the message
        err.message = 'Oh no! ' + err.message
        // attach some custom data
        err['@myCustomProperty'] = 'Hello, World!'
      })
    })
    

    The error is subsequently processed for the client following OData’s Error Response Body format. Hence, if you want to add custom properties, they must be prefixed with @ in order to not be purged.

    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.quantity > 11)  throw 'Order quantity 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.quantity)
    })
    

    Note: 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 through [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 as first and the request as second argument (that is, handler(data, req)).

    Note: The type of the result (that means, array, object, primitive, etc.) depends on the respective request.

    Example:

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

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

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

    Note: This is only meant for synchronous code due to performance. Note: Parameter each is not minifier-safe.

    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 modifications are allowed. That is, replacing the result like in the following example isn’t possible:

    this.after('READ','Books', (books)=>{
      return \<something else...\>
    })
    

    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.

    Keep in mind that all after handlers are executed in parallel via Promise.all() so they must not have side-effects!

    To replace 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.prepend (function)

    Use srv.prepend in order to register handlers, which shall be executed before already registered handlers. In particular, this can be used to override handlers from reused services as in cap/samples/fiori/srv/mashup.js:

    For example, the following would register a handler for inserting Orders that runs instead of the default handlers of the connected database service.

    const CatalogService = await cds.connect.to ('CatalogService')
    const ReviewsService = await cds.connect.to ('ReviewsService')
    
    //
    // Delegate requests to read reviews to the ReviewsService
    // Note: prepend is necessary to intercept generic default handler
    //
    CatalogService.prepend (srv => srv.on ('READ', 'Books/reviews', (req) => {
      console.debug ('> delegating request to ReviewsService')
      const [id] = req.params, { columns, limit } = req.query.SELECT
      return ReviewsService.read ('Reviews',columns).limit(limit).where({subject:String(id)})
    }))
    

    You could also use that to add support for custom behavior to generic database services:

    const db = await cds.connect('db')
    db.prepend (()=>{
      db.on ('INSERT','Orders', req => {...})
    })
    

    For Common CRUD and REST-style Events

    CAP service definitions frequently provide data-centric APIs by exposing projections on underlying domain model entities as introduced in the Getting Started guide:

    using { sap.capire.bookshop as my } from '../db/schema';
    service AdminService {
      entity Books as projection on my.Books;
      entity Authors as projection on my.Authors;
    }
    

    In effect, CAP will provide generic service providers, which serve corresponding CRUD request (for OData) which map to corresponding REST Request as follows:

    For AdminService at /admin endpoint

    REST Paths CRUD Operation
    POST /Books CREATE Books
    GET /Books READ Books
    PUT /Books UPDATE Books
    DELETE /Books DELETE Books

    In addition, CAP provides built-in support for Fiori Draft, which add additional CRUD events, like NEW, EDIT, PATCH, and SAVE. → Learn more about Fiori Drafts

    For each of which you can add custom handlers, either by specifying the CRUD operation or by specifying the corresponding REST method as follows:

    // srv/admin-service.js
    const cds = require('@sap/cds')
    module.exports = cds.service.impl (function(){
      // CRUD-style handler registration        REST-style registration
      this.on ('READ','Books', ...)   /* or: */ this.on ('GET','Books', ...) 
      this.on ('CREATE','Books', ...) /* or: */ this.on ('POST','Books', ...) 
      //...
    })
    

    For Custom Events, i.e., Actions and Functions

    In addition to the common CRUD and REST events, you can declare custom events as actions or functions.

    service CatalogService { ...
      action submitOrder (book: Books:ID, quantity: Integer);
      function getMyOrders() returns array of Orders;
    }
    

    Register handlers for these as follows:

    // srv/admin-service.js
    const cds = require('@sap/cds')
    module.exports = cds.service.impl (function(){
      this.on ('submitOrder', (req) => {...})
      this.on ('getMyOrders', (req) => {...}) 
    })
    

    For Provided and Connected Services

    Most frequently you would add event handlers to provided services. Yet, you can also add handlers to services consumed in your project, even to generic services provided by the CAP framework itself, like the default database service:

    const db = await cds.connect('db')
    db.before ('INSERT','Orders', req => {...})
    db.after ('READ','*', each => {...})
    

    For Synchronous and Asynchronous Events

    The APIs documented below are used to register handlers for synchronous requests as well as for subscription to asynchronous events.

    For example, given this service definition:

    service ReviewsService { //...
      event reviewed : { subject: String; rating: Decimal }
    }
    

    … an event subscription and handling would look like that.

    const ReviewsService = await cds.connect.to('ReviewsService')
    ReviewsService.on ('reviewed', (msg) => {...})
    

    Learn more about Messaging API

    Messaging API

    srv.emit ({ event, data?, headers? })

    This is the basic method to send both, asynchronous event messages, as well as synchronous requests. The implementation constructs an instance of [cds.Event], which is then dispatched through all registered event handlers.

    Common Usages:

    You can use srv.emit as a basic and flexible alternative to srv.run, for example to send queries plus additional request headers to remote services:

    const query = SELECT.from(Foo), tx = srv.tx(req)
    tx.run (query)       //> without custom headers
    tx.emit ({ query })  //> equivalent to tx.run()
    tx.emit ({ query, headers:{...} })
    

    A typical usage for asynchronous messages looks like that:

    this.emit ({ event:'reviewed', data:{ subject, rating }, ... })
    this.emit ('reviewed', { subject, rating }) //> see below
    

    Learn more about Event Messages. Learn more about Requests.

    Returns a Promise resolving to the response of respective event handlers.

    For results of queries, see srv.run.

    srv.emit (event, data?, headers?)

    Convenience variant to srv.emit, which allows to specify the primary properties for emitting asynchronous event messages.
    Here’s an example from cap/samples/reviews:

    this.emit ('reviewed', { subject, rating })
    

    srv.on (event, handler) this

    Subscribe to asynchronous events by registering handlers using the common srv.on() method.

    For example, given this service definition:

    service ReviewsService { //...
      event reviewed : { subject: String; rating: Decimal }
    }
    

    … an event subscription and handling would look like that.

    const ReviewsService = await cds.connect.to('ReviewsService')
    ReviewsService.on ('reviewed', (msg) => {...})
    

    There’s one major difference, though: Handlers for synchronous requests execute as interceptors, which pass control to subsequent handlers by calling next(), handlers for asynchronous events execute as listeners with all registered handlers being executed without calling next().

    REST-style API

    srv.send ({ (method, path) | query | event, data?, headers?, }) results

    This is a convenience alternative to srv.emit method to send both, asynchronous event messages, as well as synchronous requests. The implementation constructs an instance of cds.Request, which is then dispatched through all registered event handlers.

    Common Usages:

    srv.send can be used in a manner similar to srv.emit to send requests using HTTP methods and additional request headers if neccessary:

    const srv = await cds.connect.to('SomeService')
    const tx = srv.tx(req)
    const data = { ID: 111, name: 'Mark Twain' }
    const headers = {...}
    await tx.send({ method: 'POST', path: 'Authors', data, headers }) //> send HTTP request
    tx.send({ event: 'AuthorCreated', data, headers }) //> equivalent to tx.emit()
    

    Learn more about cds.Requests. Learn more about Event Messages.

    Alternatively queries in CQN notation can be used:

    const { Books, Authors } = srv.entities //> reflection
    await tx.send({ query: INSERT.into(Authors, { ID: 111, name: 'Mark Twain' }) }) //> without custom headers
    const query = [SELECT.from(Books), SELECT.from(Authors)]
    const headers = {...}
    const [books, authors] = await srv.send({ query, headers })
    

    Learn more about using queries in srv.run. Learn more about service-related reflection using srv.entities.

    Returns a Promise resolving to the response of a respective request.

    For results of queries, see srv.run.

    srv.send (method, path, data?, headers?) results

    A variant of srv.send which allows to specify the primary properties for sending requests:

    const twain = await tx.send('GET', 'Authors(111)')
    const headers = {...}
    const data = { ID: 222, title: 'The Adventures of Tom Sawyer', author: { ID: twain.ID } }
    await tx.send('POST', 'Books', data, headers)
    

    Convenient Shortcuts:

    srv.get (entity | path, data?) results

    srv.put (entity | path, data?) results

    srv.post (entity | path, data?) results

    srv.patch (entity | path, data?) results

    srv.delete (entity | path, data?) results

    These methods are HTTP method-style counterparts to the CRUD-style convenience methods. As with these, the returned queries can be executed with await.

    REST and OData services support these basic http methods, which are mapped to their CRUD counterpart as documented in the following. They can be used to construct data queries as with the CRUD variants as well as be used to send plain HTTP requests.

    Common Usages:

    await srv.get(Books).where({ID:111})
    await srv.post(Books).entries({ID:111, ...})
    await srv.patch(Books,111).with({...})
    await srv.delete(Books,111)
    

    These are equivalent to:

    await srv.read(Books).where({ID:111})
    await srv.create(Books).entries({ID:111, ...})
    await srv.update(Books,111).with({...})
    await srv.delete(Books,111)
    

    Note: UPDATE translates to PATCH, not PUT.

    Plain REST Usages:

    While the previous usage samples still constructed queries, and rather worked as using syntax aliases, you can also use these methods to send plain, arbitrary HTTP requests to remote services. Just pass a string starting with / as the first argument to do so:

    srv.post('/some/arbitrary/path', {foo:'bar'})
    srv.get('/some/arbitrary/path/111')
    srv.patch('/some/arbitrary/path', {foo:'bar'})
    srv.delete('/some/arbitrary/path/111')
    

    Querying API

    srv.run (query) results

    This is the central function to run queries. It expects a query or an array of queries in CQN notation, and returns a Promise resolving to the queries’ results.

    Common Usage:

    const srv = await cds.connect.to ('SomeService') 
    const { Books, Authors } = srv.entities //> reflection
    const books = await srv.run (SELECT.from(Books))
    const [books, authors] = await srv.run ([SELECT.from(Books), SELECT.from(Authors)])
    

    Learn more about service-related reflection using srv.entities. Learn more about srv.run variant to send native query string.

    If an array of queries is passed to srv.run, queries are run in parallel.

    Returns a Promise resolving to…

    • to a number-like object indicating affected rows in case of UPDATE, DELETE.
    • an iterable allowing to reflect on (generated) primary keys
    • an array of rows for SELECT queries

    For example, you can use results like so:

    const tx = cds.tx(req)
    const [ Emily, Charlotte ] = await tx.run (INSERT.into (Authors,[
       {name:'Emily Brontë'},
       {name:'Charlotte Brontë'},
    ]))
    await tx.run (INSERT.into(Books).columns('title','author_ID').rows(
       [ 'Wuthering Heights', Emily.ID ],
       [ 'Jayne Eyre', Charlotte.ID ],
    ))
    const books = await tx.run (SELECT.from(Books))
    const affectedRows = UPDATE(Books,books[0].ID)
      .with(`stock -=`,order.quantity)
      .where(`stock>=`,order.quantity)
    if (affectedRows < 1) req.reject(409,'Sold out, sorry')
    

    srv.run (string, args?) results

    Variant of srv.run which accepts native query strings, as understood by the receiving service, instead of CQN queries. For example, a SQL string in case of a connected SQL database.

    Common Usage:

    cds.run (`SELECT * from sap_capire_bookshop_Books`)
    cds.run (`CALL Some_Stored_Procedure (11,'foo')`)
    cds.run (`SELECT * from Some_Table_Function (11,'foo')`)
    

    You need to pass fully qualified database table names.

    Passing Arguments:

    Argument args is an optional array or object of binding parameters bound to respective placeholders in the query using one of these placeholders:

    • :name — named parameters bound to respective entries in an args object
    • ? — positional parameters bound to entries of args in order of occurrence
    cds.run ('SELECT * from Authors where name like ?',['%Poe%'])
    

    Prefer that over concatenating values into query strings to avoid SQL injection.

    Convenient Shortcuts:

    srv.read (entity, key?, projection?) SELECT query

    srv.create (entity, key?) INSERT query

    srv.update (entity, key?) UPDATE query

    srv.delete (entity, key?) DELETE query

    These methods construct queries in a fluent method-call style instead of the Embedded QL style provided by cds.ql. The returned queries can be executed with await.

    A typical usage is as follows:

    const books = await cds.read('Books').orderBy('title')
    

    Essentially, each of these 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 q = SELECT.from(entity)
      return Object.assign (q, { then:(r,e) => this.run(q).then(r,e) }
    }
    

    The following three variants to read Books are equivalent:

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

    srv.insert (data) .into (entity) INSERT query

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

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

    Actions API

    In case you declared custom actions and functions in the service definition of a connected or provided service, the respective instance of cds.Service will automatically be equipped with corresponding JavaScript methods, allowing you to write code similar to srv.create/read/....

    Unbound Actions / Functions

    For example, with that service definition (taken from cap/samples/bookshop):

    service CatalogService { ...
      action submitOrder (book: Books:ID, quantity: Integer);
    }
    

    You can invoke the declared submitOrder action from your code as follows:

    const cats = await cds.connect.to('CatalogService')
    // basic variant using srv.send
    const res1 = await cats.send('POST','submitOrder',{ book:111, quantity:1 })
    // named args variant
    const res2 = await cats.submitOrder ({ book:111, quantity:1 })
    // positional args variant
    const res3 = await cats.submitOrder (111,1)
    

    Bound Actions / Functions

    Bound actions have two implicit leading arguments with the target entity’s name and primary key. Assumed we had a bound action like that:

    service CatalogService { ...
      entity Books { ... } actions {
        action submitOrder (quantity: Integer);
      }
    }
    

    Then, you’d invoke that as follows:

    const cats = await cds.connect.to('CatalogService')
    const res3 = await cats.submitOrder ('Books',111,1)
    

    Streaming API

    srv.stream (column) .from (entity) .where (filter) Readable Stream

    This method allows streaming binary data properties. It returns a read stream which can be used to pipe to write streams, as shown in the following examples.

    const stream = srv.stream().from('T', {ID: 1}, a => a.data)
    stream.pipe(process.stdout)
    
    const stream = srv.stream('data').from('T', {ID: 1})
    stream.pipe(process.stdout)
    
    const stream = srv.stream('data').from('T').where({ID: 1})
    stream.pipe(process.stdout)
    

    srv.stream (cqn) Promise < Readable Stream >

    This is a variant of srv.stream, which accepts a SELECT query as input and returns a Promise resolving to result stream when the query matched to an existing row in the database. The query is expected to select a single column and a single data row. Otherwise, an error is thrown.

    const stream = await srv.stream( SELECT('image').from('Foo',111) )
    stream.pipe(process.stdout)
    

    srv.foreach (entity | query, args?, callback) Promise

    Executes the statement and processes the result set one row by one. Use that instead of cds.run if you expect large result sets as processes it in a streaming-like fashion instead of materializing the full set in memory before.

    Common Usages:

    cds.foreach (SELECT.from('Foo'), each => console.log(each))
    cds.foreach ('Foo', each => console.log(each))
    

    As depicted in the second line, a plain entity name can be used for the entity argument in which case it’s expanded to a SELECT * from ....

    cds.ApplicationService

    → click on heading

    cds.DatabaseService

    → click on heading

    cds.RemoteService

    → click on heading

    Show/Hide Beta Features