Skip to content

06. CRUD handlers

Nikica Josipovic edited this page Jul 7, 2025 · 5 revisions

Implementing CRUD Event Handlers

Let's create another file with the name before-CREATE.js in the folder srv/ProcesorService/Incidents.

module.exports = async function createIncident (req) {

  const  { Customers } = this.entities

  const res = await SELECT.one
    .from(Customers)
    .columns('status')
    .where({ ID: req.data.customer_ID })
  
  if (res?.status === 'Gold') {
    req.data.urgency_code = 'H'
  }
}

You can test this by upgrading one of the customers to Gold and then creating incidents with urgency set to Low or Medium for different customers. If a customer is Gold then urgency should be automatically set to High.

What are we doing here?

When you create a new Incident, the customer status is a readonly attribute from an associated entity, so it is empty at request time.

We need to select the customer status with a query first. In this example, we leverage the convenience function this.entities, which can also be useful at debug time. The rest is quite simple: When the customer is golden, urgency has to be set to high, which happens by directly modifying req.data. Please keep in mind, just like normal CAP event handlers, before handlers are executed before any writes to the database, so at this time, you could e.g. select the before-image of a record. Also, all callbacks run within the transaction context of the request, meaning all updates to data will automatically roll-back when errors occur somewhere down the event-handler chain (e.g. a constraint is violated, or some other handler rejects the request).

Another handler

Wea are missing another file with the name before-UPDATE.js in the folder srv/ProcesorService/Incidents.

module.exports = async function updateIncident (req) {
  if (!req.data.customer_ID) return

  const  { Customers } = this.entities

  const res = await SELECT.one
    .from(Customers)
    .columns('status')
    .where({ ID: req.data.customer_ID })
  
  if (res.status === 'Gold' && req.data.urgency_code !== 'H') 
    req.reject(400, 'Urgency code must be "H" for Gold customers')
}

What are we doing here?

The most notable difference here is the first line of the event handler. When creating a record, we usually get a full record image in req.data which we can utilize. In the update case though, you usually have the delta of a record to work with. When an incident is updated, and the customer does not change, then we simply do not need to act at all, hence the return statement. Unlike the create handler, this example shows how to reject a request when the requirements are not met, instead of automatically changing the priority.

Playing around

Let's create a new incident without a customer. You will get an error-message, because the select statement will try to use and undefined value req.data.customer_ID. We could add the following line to the top of the before_CREATE.js file

  if (!req.data.customer_ID) req.reject(400, 'A customer must be selected to create an incident')

to get an error message for a constraint violation. You could also remove the one qualifier from the select statement to observe, that the QL result changes to an array.

To make the UI even more centered around the customer status, we could also create an after-READ.js for incidents, where we

module.exports = async function readIncident (result, req) {
  for (const row of result) {
    if (!row.customer) continue;
    row.customer.name += ' (' + row.CustomerStatus + ')';
  }
}
Clone this wiki locally