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.
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(options) {
// prepare express app
const o = { ...options, __proto__:defaults }
const app = cds.app = o.app || require('express')()
cds.emit ('bootstrap', app)
// mount static resources and middlewares
if (o.cors) app.use (o.cors) //> if not in prod
if (o.health) app.get ('/health', o.health)
if (o.static) app.use (express.static (o.static)) //> defaults to ./app
if (o.favicon) app.use ('/favicon.ico', o.favicon) //> if none in ./app
if (o.index) app.get ('/',o.index) //> if none in ./app and not in prod
// 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()
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
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
}
req
!= req
The req
object in your express middleware is not the same as req
in your CDS event handlers.
Lifecycle Events
bootstrap
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
Emitted whenever a CDS model got loaded using cds.load()
connect
Emitted for each service constructed through cds.connect
.
serving
Emitted for each service constructed by cds.serve
.
served
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
A one-time event, emitted when the server has been started and is listening to incoming requests.
shutdown
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
})
Configuration
The behavior of the built-in server.js
can be customized through the options documented in the following sections.
CORS Middleware
The built-in CORS middleware can be enabled explicitly with cds.server.cors: true
. By default, this is false
if in production.
Learn more about best practices regarding Cross-Origin Resource Sharing (CORS).
Toggle Generic Index Page
The default generic index.html page is not served if NODE_ENV
is set to production
. Set cds.server.index: true
to restore the generic index page in production.
See the Generic index.html page in action.
Maximum Request Body Size
There are two ways to restrict the maximum request body size of incoming requests, globally for all endpoints and for individual services. If the payload exceeds the configured value, the request is rejected with 413 - Payload too large. The configured values are passed through to the underlying Express body parser middlewares. Therefore, the default limit is 100kb, as this is the default of the Express built-in body parsers.
The maximum request body size can be limited globally, for all services and protocols, using the configuration cds.server.body_parser.limit
, like so:
{
"cds": {
"server": {
"body_parser": {
"limit": "1mb" // also accepts b, kb, etc...
}
}
}
}
To restrict the maximum request body size of requests received by an individual service, the service specific annotation @cds.server.body_parser.limit
can be used, like so:
annotate AdminService with @cds.server.body_parser.limit: '1mb';
This is useful when the expected request body sizes might vary for services within the application. If both the global configuration and the service specific annotation are set, the service specific annotation takes precedence for the respective service.
See Also...
The cds-plugin
package technique provides more options to customize server startup.