Adding Custom Logic

Objective

After completing this lesson, you will be able to implement programmatic validations with the help of .before event handlers

Custom Service Implementations

As most standard tasks and use cases are covered by generic service providers, the need to add service implementation code is greatly reduced and minified.

The remaining cases reduce to real custom logic, specific to your domain and application, such as domain-specific programmatic validations.

As an example, let's program a validation that ensures that the date of birth passed is before the date of death passed when creating a new author via the AdminService.

The easiest way to add custom service implementations is to simply place an equally named .js file next to the .cds file containing the respective service definition. I.e. in our case we place a file named admin-service.js in the srv folder of our project, as this folder also contains the file admin-service.cds, which is used to define the AdminService (see following figure).

Note

Alternatively, you can also annotate the service definition in the .cds file with the @impl annotation. You use this annotation to explicitly refer to another .js file that should contain the service implementation.

There are different ways to implement the required custom logic in the .js file. The most common way is to use a subclass of cds.ApplicationService to benefit from generic out-of-the-box implementations. This is because the cds.ApplicationService class is the default service provider implementation, which adds the generic providers already discussed to a service definition.

To create a subclass of cds.ApplicationService, we first assign the @sap/cds module to the constant cds in our code (line 1). The @sap/cds module is the cds facade object that provides access to all CAP Node.js APIs.

We then create a subclass of cds.ApplicationService in lines 4 to 6, in which we will later implement our custom logic. The name of the subclass is arbitrary. We call it here like our service interface, i.e. AdminService.

In line 9, we make the AdminService class available for import into other modules. This is common practice in Node.js to structure code.

Note

If you now start the application by entering cds watch in the terminal, the added service implementation file is displayed in the log via an output similar to the following:

Code snippet
[cds] - serving AdminService { path: '/admin', impl: 'srv/admin-service.js' }
Expand

Event Handlers

Custom service implementations are carried out with the help of custom event handlers, whereby a distinction is made between different types of handlers.

Watch the video to get an overview of the different event handlers.

Registering .before Event Handlers

For the validation we want to implement, we need a before handler that should be registered for CREATE and UPDATE operations on the Authors entity.

To register custom event handlers, overwrite the inherited init() method in the created implementation class (see following figure). Do not forget to call super.init() in the implementation to allow subclasses to register their handlers.

To register a before handler in the init() method, call the inherited before() method via this.before(). To do this, you must supply the following interface. A detailed description of the individual parameters can be found in the CAP documentation:

Code Snippet
12345
function before ( event : string | string[] | '*', entity? : CSN definition | CSN definition[] | string | string[] | '*', handler : function )

In the example shown, the named function this.validateLifeData is registered for CREATE and UPDATE operations on the Authors entity. We will discuss the implementation of this function later.

The entity or entities for which the registration is to take place can be passed to the before() method as string. However, it is recommended to pass CSN definitions here instead. The CSN definitions of the entities that are exposed via the service are conveniently made available via the this.entities property.

In the example shown, the required CSN definition for the Authors entity is assigned to the Authors constant via a destructuring assignment.

Note

The destructuring assignment syntax is JavaScript standard. It is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.

Validating Inbound Data

Finally, let's take a look at how the validateLifeData function that has been registered as a before handler can be implemented on the AdminService class.

Before handlers generally receive a single argument, an instance of cds.Request. This instance provides properties and methods that can be used to implement the handler. In the example, we have named the argument req (see following figure).

For CREATE and UPDATE requests, which we are concerned with here, the data property of the interface parameter contains the HTTP body of the request. We use a destructuring assignment to assign the passed values from the HTTP body to the constants dateOfBirth and dateOfDeath.

If the date of death is before the date of birth, we issue a corresponding error message via the req.error() method.

Note

The req.error() method collects all failures in property req.errors, allowing to display them on UIs all at once. If there are req.errors after the before phase, request processing is aborted with a corresponding error response returned to the client.

Hint

The literal passed to the req.error() method is delimited with backtick (`) characters. Such template literals allow variable values to be embedded in a string using the syntax ${name_of_the_variable}.

Demonstration & Exercise: Provide a .before Event Handler

Note

As exercise, carry out the step-by-step instructions in the following demonstration yourself in the SAP Business Application Studio.

As a starting point for the exercise, use the outcome of the previous exercise Define a Service Based on Denormalized Views if you have successfully completed it. Alternatively, you can also use the branch 10_denormalized_views from the following GitHub repository as a starting point:

https://github.com/SAP-samples/cap-development-learning-journey

The complete implementation of the simulation can be found in the 11_.before_event_handler branch of the GitHub repository.

Detailed information on the content of the repository and how to use it can be found here.

Watch the video to see how to provide a .before event handler.

Log in to track your progress & complete quizzes