sapui5tutors SAPUI5 • Fiori • SAP BTP Step-by-step tutorials Real project examples Interview Q&A
Practical SAPUI5 • Fiori • SAP BTP tutorials and interview prep

CAPM Event Handlers: Before, On, After Explained

The request lifecycle in three phases

Every CAP request flows through three handler phases: before, on, and after. Understanding which phase does what is the single most important concept for writing CAP backend logic.

before validates and enriches. on implements. after transforms the result. Memorize that sentence.


before: validate and enrich input

this.before('CREATE', 'Orders', async (req) => {
  const { quantity } = req.data;
  if (quantity <= 0) {
    req.error(400, 'Quantity must be positive');
  }
  req.data.createdBy = req.user.id;
});

before handlers run before the database operation. Use them for input validation (req.error aborts the request) and for stamping data the client shouldn't set.


on: provide the implementation

this.on('READ', 'Books', async (req) => {
  // custom read logic, e.g. from an external source
  return fetchBooksFromCatalog(req.query);
});

Registering an on handler replaces the default implementation. If you don't register one, CAP's generic handler does the database work for you. Most handlers you'll write are before and after — the defaults handle the middle.

Only write on handlers when the default behavior is wrong — external data sources, computed results, special persistence.


after: shape the output

this.after('READ', 'Orders', (orders) => {
  orders.forEach(o => {
    o.totalAmount = o.items.reduce((s, i) => s + i.price * i.quantity, 0);
  });
});

after handlers receive the result and can enrich or reshape it. Computed fields, formatting, and adding virtual properties belong here.


Handler ordering and specificity

  • More specific wins — this.before('CREATE', 'Orders') runs before this.before('CREATE', '*').
  • Multiple handlers chain — several before handlers on the same event all run, in registration order.
  • req.error vs req.reject — error adds a message and continues collecting; reject aborts immediately.
  • Async everywhere — handlers can be async; CAP awaits them in order.

Common handler patterns

Defaulting: set values the client didn't provide in before CREATE — status fields, timestamps, generated numbers. The client sends business data; the handler completes it.

Cascading: in after CREATE on a parent, create related child records. Keep it in the same transaction so partial failures roll back cleanly.

Guarding: in before UPDATE, compare req.data against the current record and reject illegal transitions — e.g. changing an order's customer after approval.

Handlers should be small and single-purpose. A 200-line before UPDATE is a design smell — split it into focused handlers.


Put logic in the right phase

Validation in before, defaults in before, custom persistence in on, computed output in after. When logic feels awkward, it's usually in the wrong phase.

Confused about a handler scenario? Lay it out in the comments.