cap-handler-framework
v1.1.0
Published
Handler framework for SAP CAP applications with auto-generation, multi-service support, TypeScript, and draft lifecycle
Maintainers
Readme
cap-handler-framework
Handler framework for SAP CAP applications with multi-service support, TypeScript, and full draft lifecycle.
✨ Features
- ✅ Convention-based - Auto-maps methods like
beforeCreate,onRead,afterUpdate - ✅ Multi-service - Support for unlimited CAP services
- ✅ Draft lifecycle - Full support for draft entities (NEW, EDIT, SAVE, CANCEL, etc.)
- ✅ Type-safe - Full TypeScript support with type definitions
- ✅ Performance - ExpandTree optimization (50-80% fewer calls)
- ✅ Auto-discovery - Handlers automatically discovered via cds-plugin
- ✅ Dependency injection - Shared context for services and utilities
- ✅ Factory pattern - Cross-handler communication support
📦 Installation
npm install cap-handler-framework🚀 Quick Start
1. Create Handler
// srv/my-service/handlers/entities/BooksHandler.ts
import { BaseHandler, TypedRequest } from 'cap-handler-framework';
export default class BooksHandler extends BaseHandler {
getEntityName() {
return 'Books';
}
async beforeCreate(req: TypedRequest): Promise<void> {
req.data.createdAt = new Date();
}
async onRead(req: TypedRequest, next: () => Promise<any>): Promise<any> {
this.initializeExpandTree(req);
const result = await next();
if (this.isExpanded('author')) {
await this.enrichAuthor(result);
}
return result;
}
}2. Start Service
cds watchThat's it! Handlers are automatically registered. ✅
📖 Documentation
- Developer Guide - Complete tutorial and implementation guide
- Quick Reference - Cheat sheet for common patterns
- Factory Pattern Usage - Cross-handler communication guide
- Framework Comparison - Comparison with other handler patterns
🎯 Convention-Based Method Mapping
| CDS Event | Handler Method |
|-----------|----------------|
| before('CREATE') | beforeCreate(req) |
| on('READ') | onRead(req, next) |
| after('UPDATE') | afterUpdate(data, req) |
| before('DELETE') | beforeDelete(req) |
🎨 Draft Support
export default class OrdersHandler extends BaseHandler {
shouldHandleDrafts() {
return true;
}
async beforeSaveDraft(req: TypedRequest): Promise<void> {
// Validate before activation
if (!req.data.customer_ID) {
req.error(400, 'Customer is required');
}
}
async afterPatchDraft(data: any, req: TypedRequest): Promise<void> {
// Auto-compute on field change
data.total = data.quantity * data.unitPrice;
}
}⚡ Bound Actions
async onBorrow(req: TypedRequest): Promise<any> {
const { ID } = req.params[0]; // Entity key
const { days } = req.data; // Parameters
// Your logic
return updatedEntity;
}🔌 External Services
// srv/my-service/handlers.config.json
{
"externalServices": ["API_BUSINESS_PARTNER"]
}const bpApi = this.getExternalService('API_BUSINESS_PARTNER');
const result = await bpApi.run(SELECT.from('A_BusinessPartner').where(...));🏭 Cross-Handler Communication
import { HandlerFactory } from 'cap-handler-framework';
const factory = HandlerFactory.getInstance();
const otherHandler = factory.getTradeSlipsHandler();
await otherHandler.somePublicMethod(data);📁 Project Structure
srv/
└── my-service/
├── my-service.cds
├── handlers.config.json (optional)
└── handlers/
├── entities/
│ └── BooksHandler.ts
├── proxies/
│ └── ExternalServiceProxy.ts
└── operations/
└── customAction.ts🎓 Learning Resources
- Start with Quick Reference (10 min)
- Follow Developer Guide tutorial (30 min)
- Explore example handlers in the repo
📝 License
MIT
🤝 Contributing
Contributions welcome! Please read our contributing guidelines first.
🐛 Issues
Found a bug? Report it here
