BTP CAP Part 6: Service Handlers — Before, On, After and Transactions
Add business logic the CAP way: event handlers in before, on and after phases, validations with req. error/req. reject, enriching results, calling other services and remote APIs, transaction handling with cds. tx, and avoiding the anti-pattern of replacing generic handlers.
Explanation
📌 KEY POINTS
✅ before: validate or modify input; on: implement (or replace) the operation; after: enrich or adjust results.
✅ Use req.error/req.reject for validation errors with proper HTTP status codes.
✅ Each request runs in a transaction; cds.tx controls explicit transactions for background work.
✅ Remote services are consumed with cds.connect.to and CQL queries.
⚠️ Writing on-READ handlers that re-implement generic reads breaks paging, filtering and $expand.
━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE
Part 5 modeled the domain. Now logic.
━━━━━━━━━━━━━━━━━━━━
▶ 1. PHASES
- before, on, after.
━━━━━━━━━━━━━━━━━━━━
▶ 2. VALIDATION
- req.error (collect), req.reject (stop).
━━━━━━━━━━━━━━━━━━━━
▶ 3. ENRICHMENT
- after READ computations.
━━━━━━━━━━━━━━━━━━━━
▶ 4. TRANSACTIONS
- Request transaction; cds.tx for jobs.
━━━━━━━━━━━━━━━━━━━━
▶ 5. REMOTE CALLS
- cds.connect.to('API_BUSINESS_PARTNER').
━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP
- Add a before CREATE validation.
- Add an after READ enrichment.
- Call a remote service in an action.
✅ CHECKPOINT: invalid data rejected with 400; paging still works.
Code example
===== srv/cat-service.js =====const cds = require('@sap/cds')module.exports = class CatalogService extends cds.ApplicationService { init() { const { Books } = this.entities this.before('CREATE', Books, req => { if (req.data.stock < 0) req.reject(400, 'Stock must not be negative') }) this.after('READ', Books, books => { for (const b of books) if (b.stock > 100) b.title += ' (in stock)' }) this.on('submitOrder', async req => { const { book, qty } = req.data const n = await UPDATE(Books, book).with`stock = stock - ${qty}`.where`stock >= ${qty}` if (!n) return req.error(409, 'Insufficient stock') }) return super.init()}}Real project scenario
A team wrote custom on-READ handlers for every entity to add a computed field; $filter and paging broke in the Fiori app. Replacing them with after-READ enrichment restored generic behavior.
Common mistakes
- On-READ reimplementations.
- Throwing raw errors.
- Remote calls per row.
- Ignoring transactions.
- Business logic in UI.
Best practices
- before/after first.
- req.error/reject.
- Batch remote calls.
- cds.tx where needed.
- Logic in service layer.
Interview angle
Questions: "before vs on vs after?", "How do you validate input?", "How do transactions work in CAP?"
Strong answer: phases, validation, enrichment, transactions, remote calls.
Practice this lesson
Quick check
1. In a CAP Node.js service implementation, what is the correct execution order of the event handler phases for a single incoming request?
2. Which handler phase is best for input validation in CAP?
3. Custom on-READ handlers for every entity broke $filter and paging. What is the better approach?
Interview questions
1. Suppose you inherit an existing CAP solution. How would you review it before making a change?
Answer it in your own words first, then compare.
2. Custom CAP handlers broke Fiori paging and filtering, and a remote call per row made reads slow. How do you restructure the logic?
Answer it in your own words first, then compare.
Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.
More lessons in this topic
- BTP CAP Part 1: What CAP Is and Why It Matters on SAP BTP
- BTP CAP Part 2: Building and Exposing Services with CDS
- BTP CAP Part 3: Securing CAP Services with XSUAA Role-Based Authorization
- BTP CAP Part 4: Multitenancy, Extensibility and Production Deployment
- BTP CAP Part 5: Domain Modeling with Entities, Associations, Compositions and Aspects
- BTP CAP Part 7: OData Services, Actions, Functions and Draft