Events and Requests
cds. context
This property provides seemingly static access to the current cds.EventContext
, that is the current tenant
, user
, locale
, etc. from wherever you are in your code. For example:
let { tenant, user } = cds.context
Usually that context is set by inbound middlewares.
The property is realised as a so-called continuation-local variable, implemented using Node.js' async local storage technique, and a getter/setter pair: The getter is a shortcut forgetStore()
. The setter coerces values into valid instances of cds.EventContext
. For example:
cds.context = { tenant:'t1', user:'u2' }
let ctx = cds.context
ctx instanceof cds.EventContext //> true
ctx.user instanceof cds.User //> true
ctx.tenant === 't1' //> true
ctx.user.id === 'u2' //> true
If a transaction object is assigned, it's tx.context
will be used, hence cds.context = tx
acts as a convenience shortcut for cds.context = tx.context
:
let tx = cds.context = cds.tx({ ... })
cds.context === tx.context //> true
TIP
Prefer local req
objects in your handlers for accessing event context properties, as each access to cds.context
happens through AsyncLocalStorage.getStore()
, which induces some minor overhead.
Class cds.EventContext
Instances of this class represent the invocation context of incoming requests and event messages, such as tenant
, user
and locale
. Classes cds.Event
and cds.Request
inherit from it and hence provide acccess to the event context properties:
this.on ('*', req => {
let { tenant, user } = req
...
})
In addition, you can access the current event context from wherever you are in your code via the continuation-local variable cds.context
:
let { tenant, user } = cds.context
. http
If the inbound process came from an HTTP channel, this property provides access to express's common req
and res
objects. The property is propagated from cds.context
to all child requests. So, on all handlers, even the ones in your database services, you can always access that property like so:
this.on ('*', req => {
let { res } = req.http
res.send('Hello!')
})
. id
A unique string used for request correlation.
For inbound HTTP requests the implementation fills it from these sources in order of precedence:
x-correlation-id
headerx-correlationid
headerx-request-id
headerx-vcap-request-id
header- a newly created UUID
On outgoing HTTP messages it is propagated as x-correlation-id
header.
For inbound CloudEvents messages it taken from the id
context property and propagated to the same on outgoing CloudEvents messages.
. locale
The current user's preferred locale, usually taken from HTTP Accept-Language header of incoming requests and resolve to normalized.
. tenant
A unique string identifying the current tenant, or undefined
if not in multitenancy mode. In case of multitenant operation, this string is used for tenant isolation, for example as keys in the database connection pools.
. timestamp
A constant timestamp for the current request being processed,as an instance of Date
. The CAP framework uses that to fill in values for the CDS pseudo variable $now
, with the guaranteed same value.
Learn more in the Managed Data guide.
. user
The current user, an instance of cds.User
as identified and verified by the authentication strategy. If no user is authenticated, cds.User.anonymous
is returned.
See reference docs for cds.User
.
Class cds.Event
Class cds.Event
represents event messages in asynchronous messaging, providing access to the event name, payload data, and optional headers. It also serves as the base class for cds.Request
and hence for all synchronous interactions.
. event
The name of the incoming event, which can be one of:
- The name of an incoming CRUD request like
CREATE
,READ
,UPDATE
,DELETE
- The name of a custom action or function like
submitOrder
- The name of a custom event like
OrderedBook
. data
Contains the event data. For example, the HTTP body for CREATE
or UPDATE
requests, or the payload of an asynchronous event message.
. headers
Provides access to headers of the event message or request. In case of asynchronous event messages, it's the headers information sent by the event source. For HTTP requests it's the standard Node.js request headers.
eve. before 'commit'
eve. on 'succeeded'
eve. on 'failed'
eve. on 'done'
Register handlers to these events on a per event / request basis. The events are executed when the whole top-level request handling is finished
Use this method to register handlers, executed when the whole request is finished.
req.before('commit', () => {...}) // immediately before calling commit
req.on('succeeded', () => {...}) // request succeeded, after commit
req.on('failed', () => {...}) // request failed, after rollback
req.on('done', () => {...}) // request succeeded/failed, after all
DANGER
The events succeeded
, failed
and done
are emitted after the current transaction ended. Hence, they run outside framework-managed transactions, and handlers can't veto the commit anymore.
To veto requests, either use the req.before('commit')
hook, or service-level before
COMMIT
handlers.
To do something which requires databases in succeeded
/failed
handlers, use cds.spawn()
, or one of the other options of manually-managed transactions.
Additional note about OData: For requests that are part of a changeset, the events are emitted once the entire changeset was completed. If at least one of the requests in the changeset fails, following the atomicity property ("all or nothing"), all requests fail.
Class cds.Request
Class cds.Request
extends cds.Event
with additional features to represent and deal with synchronous requests to services in event handlers, such as the query, additional request parameters, the authenticated user, and methods to send responses.
. _
Provides access to original inbound protocol-specific request objects. For events triggered by an HTTP request, it contains the original req
and res
objects as obtained from express.js.
WARNING
Please refrain from using internal properties of that object, that is the ones starting with '_'. They might be removed in any future release without notice.
. method
The HTTP method of the incoming request:
msg.event | → | msg.method |
---|---|---|
CREATE | → | POST |
READ | → | GET |
UPDATE | → | PATCH |
DELETE | → | DELETE |
. target
Refers to the current request's target entity definition, if any; undefined
for unbound actions/functions and events. The returned definition is a linked definition as reflected from the CSN model.
In case of OData navigation requests along associations, msg.target
refers to the last target. For example:
OData Request | req.target |
---|---|
Books | AdminService.Books |
Books/201/author | AdminService.Authors |
Books(201)/author | AdminService.Authors |
See also req.path
to learn how to access full navigation paths.See Entity Definitions in the CSN reference.Learn more about linked models and definitions.
. path
Captures the full canonicalized path information of incoming requests with navigation. If requests without navigation, req.path
is identical to req.target.name
(or req.entity
, which is a shortcut for that).
Examples based on cap/samples/bookshop AdminService:
OData Request | req.path | req.target.name |
---|---|---|
Books | AdminService.Books | AdminService.Books |
Books/201/author | AdminService.Books/author | AdminService.Authors |
Books(201)/author | AdminService.Books/author | AdminService.Authors |
. entity
This is a convenience shortcut to msg.target.name
.
. params
Provides access to parameters in URL paths as an iterable with the contents matching the positional occurrence of parameters in the url path. In case of compound parameters, the respective entry is the key value pairs as given in the URL.
For example, the parameters in an HTTP request like that:
GET /catalog/Authors(101)/books(title='Eleonora',edition=2) HTTP/1.1
The provided parameters can be accessed as follows:
const [ author, book ] = req.params
// > author === 101
// > book === { title: 'Eleonora', edition: 2 }
. query
Captures the incoming request as a CQN query object. For example, an HTTP request like GET http://.../Books
would be captured as follows:
req.query = {SELECT:{from:{ref:['Books']}}}
If bound custom operations req.query
contains the query to the entity, on which the bound custom operation is called. For unbound custom operations req.query
contains an empty object.
. subject
Acts as a pointer to one or more instances targeted by the request. It can be used as input for cds.ql as follows:
SELECT.one.from(req.subject) //> returns single object
SELECT.from(req.subject) //> returns one or many in array
UPDATE(req.subject) //> updates one or many
DELETE(req.subject) //> deletes one or many
It's available for CRUD events and bound actions.
req. reply()
Stores the given results
in req.results
, which will then be sent back to the client, rendered in a protocol-specific way.
req. reject()
Rejects the request with the given HTTP response code and single message. Additionally, req.reject
throws an error based on the passed arguments. Hence, no additional code and handlers will be executed once req.reject
has been invoked.
Arguments are the same as for req.error
req. error()
req. warn()
req. info()
req. notify()
Use these methods to collect messages or errors and return them in the request response to the caller. The method variants reflect different severity levels, use them as follows:
Variants
Method | Collected in | Typical UI | Severity |
---|---|---|---|
req.notify() | req.messages | Toasters | 1 |
req.info() | req.messages | Dialog | 2 |
req.warn() | req.messages | Dialog | 3 |
req.error() | req.errors | Dialog | 4 |
Note: messages with a severity less than 4 are collected and accessible in property req.messages
, while error messages are collected in property req.errors
. The latter allows to easily check, whether errors occurred with:
if (req.errors) //> get out somehow...
Arguments
code
Number (Optional) - Represents the error code associated with the message. If the number is in the range of HTTP status codes and the error has a severity of 4, this argument sets the HTTP response status code.message
String | Object | Error - See below for details on the non-string version.target
String (Optional) - The name of an input field/element a message is related to.args
Array (Optional) - Array of placeholder values. See Localized Messages for details.
Using an Object as Argument
You can also pass an object as the sole argument, which then contains the properties code
, message
, target
, and args
. Additional properties are preserved until the error or message is sanitized for the client. In case of an error, the additional property status
can be used to specify the HTTP status code of the response.
req.error ({
code: 'Some-Custom-Code',
message: 'Some Custom Error Message',
target: 'some_field',
status: 418
})
Additional properties can be added as well, for example to be used in custom error handlers.
In OData responses, notifications get collected and put into HTTP response header
sap-messages
as a stringified array, while the others are collected in the respective response body properties (→ see OData Error Responses).
Error Sanitization
In production, errors should never disclose any internal information that could be used by malicious actors. Hence, we sanitize all server-side errors thrown by CAP framework. That is, all errors with a 5xx status code (the default status code is 500) are returned to the client with only the respective generic message (example: 500 Internal Server Error
). Errors defined by app developers are not sanitized and returned to the client unchanged.
Additionally, the OData protocol specifies which properties an error object may have. If a custom property shall reach the client, it must be prefixed with @
in order to not be purged.
req. diff()
Use this asynchronous method to calculate the difference between the data on the database and the passed data (defaults to req.data
, if not passed).
This will trigger database requests.
const diff = await req.diff()