| status | released |
|---|
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]]
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"
}
}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)
}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.
The express.js app constructed by the server implementation.
The CLI command cds serve optionally bootstraps from project-local ./server.js or ./srv/server.js.
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', ...)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.
:::
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'))
})Emitted whenever a CDS model got loaded using cds.load()
Emitted for each service constructed through cds.connect.
Emitted for each service constructed by cds.serve.
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.
A one-time event, emitted when the server has been started and is listening to incoming requests.
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.
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
})The cds-plugin package technique provides more options to customize server startup.