Skip to content

Latest commit

 

History

History
227 lines (134 loc) · 5.65 KB

File metadata and controls

227 lines (134 loc) · 5.65 KB
status released

Bootstrapping Servers

CAP Node.js servers are bootstrapped through a built-in server.js module, which can be accessed through cds.server. You can plug-in custom logic to the default bootstrapping choreography using a custom server.js in your project.

[[toc]]

CLI Command cds serve

A Node.js CAP server process is usually started with the cds serve CLI command, with cds run and cds watch as convenience variants.

For deployment, when the @sap/cds-dk package providing the cds CLI executable is not available, use the cds-serve binary provided by the @sap/cds package:

{
  "scripts": {
    "start": "cds-serve"
  }
}

Built-in server.js

The built-in server.js constructs an express.js app, and bootstraps all CAP services using cds.connect and cds.serve. Its implementation essentially is as follows:

const cds = require('@sap/cds')
module.exports = async function cds_server() {

  // prepare express app
  const app = cds.app = require('express')()
  cds.emit ('bootstrap', app)

  // load and prepare models
  const csn = await cds.load('*') .then (cds.minify)
  cds.model = cds.compile.for.nodejs (csn)
  cds.emit ('loaded', cds.model)

  // connect to essential framework services
  if (cds.requires.db) cds.db = await cds.connect.to ('db') .then (_init)
  if (cds.requires.messaging)   await cds.connect.to ('messaging')

  // serve all services declared in models
  await cds.serve ('all') .in (app)
  await cds.emit ('served', cds.services)

  // start http server
  const port = o.port || process.env.PORT || 4004
  return app.server = app.listen (port)
}

cds. server() {.method}

This is essentially a shortcut getter to require('@sap/cds/server'), that is, it loads and returns the built-in server.js implementation. You'd mainly use this in custom server.js to delegate to the default implementation, as shown below.

cds. app {.property}

The express.js app constructed by the server implementation.

Custom server.js

The CLI command cds serve optionally bootstraps from project-local ./server.js or ./srv/server.js.

Plug-in to Lifecycle Events

In custom server.js, you can plugin to all parts of @sap/cds. Most commonly you'd register own handlers to lifecycle events emitted to the cds facade object as below:

const cds = require('@sap/cds')
// react on bootstrapping events...
cds.on('bootstrap', ...)
cds.on('served', ...)

Override cds.server()

Provide an own bootstrapping function if you want to access and process the command line options. This also allows you to override certain options before delegating to the built-in server.js. In the example below, we construct the express.js app ourselves and fix the models to be loaded.

const cds = require('@sap/cds')
// react on bootstrapping events...
cds.on('bootstrap', ...)
cds.on('served', ...)
// handle and override options
module.exports = (o)=>{
  o.from = 'srv/precompiled-csn.json'
  o.app = require('express')()
  return cds.server(o) //> delegate to default server.js
}

::: tip req != req The req object in your express middleware is not the same as req in your CDS event handlers. :::

Lifecycle Events

bootstrap {.event}

A one-time event, emitted immediately after the express.js app has been created and before any middleware or CDS services are added to it.

const cds = require('@sap/cds')
const express = require('express')
cds.on('bootstrap', (app)=>{
  // add your own middleware before any by cds are added

  // for example, serve static resources incl. index.html
  app.use(express.static(__dirname+'/srv/public'))
})

loaded {.event}

Emitted whenever a CDS model got loaded using cds.load()

connect {.event}

Emitted for each service constructed through cds.connect.

serving {.event}

Emitted for each service constructed by cds.serve.

served {.event}

A one-time event, emitted when all services have been bootstrapped and added to the express.js app.

const cds = require('@sap/cds')
cds.on('served', (services)=>{
  // We can savely access service instances through the provided argument:
  const { CatalogService, db } = services
  // ...
})

This event supports asynchronous event handlers.

listening {.event}

A one-time event, emitted when the server has been started and is listening to incoming requests.

shutdown {.event}

A one-time event, emitted when the server is closed and/or the process finishes. Listeners can execute cleanup tasks.

This event supports asynchronous event handlers.

Event Handlers

Synchronous vs. asynchronous

Unless otherwise noted, event handlers execute synchronously in the order they are registered. This is due to cds.on() and cds.emit() using Node's EventEmitter contract.

In other words this asynchronous handler code does not work as expected:

cds.on ('bootstrap', async ()=> {
  await asyncCode() // [!code error] // will NOT be awaited
}

You can use the served event's asynchronous nature though to wait for such bootstrap code:

let done
cds.on('bootstrap', ()=> {
  done = asyncCode()
}
cds.on('served', async ()=> {
  await moreCode()
  await done
})

See Also...

The cds-plugin package technique provides more options to customize server startup.