v4.0.0: Major Release: Webhook & Event-Driven Routing
This major release introduces powerful new features for handling webhook events and event-driven architectures, including multiple handler execution, priority-based resolution, and async support.
⚠️ Breaking Changes
resolve() Method Behavior Change
Important: The resolve() method now returns the highest priority handler instead of the first matching handler based on registration order.
Before (v2.x)
const resolver = new Resolver(handler1, handler2, handler3);
const result = resolver.resolve('type'); // Returns handler1 (first registered)After (v4.0.0)
// Without priority - behavior unchanged (returns first matching handler)
const resolver = new Resolver(handler1, handler2, handler3);
const result = resolver.resolve('type'); // Still returns handler1
// With priority - returns highest priority handler
class HighPriority implements PrioritizedResolveTarget {
priority = 100;
// ...
}
class LowPriority implements PrioritizedResolveTarget {
priority = 10;
// ...
}
const resolver = new Resolver(lowPriority, highPriority);
const result = resolver.resolve('type'); // Returns highPriority (priority: 100)Migration Guide: If you rely on registration order and don't want priority-based resolution:
- Continue using handlers without the
priorityproperty - they will maintain registration order (all have default priority of 0) - Or explicitly set the same priority on all handlers to maintain registration order
✨ New Features
1. Multiple Handler Execution
Execute all matching handlers for a single event type. Perfect for webhook fanout patterns where one event needs multiple processors.
import Resolver from 'class-resolver';
import { ResolveTarget } from 'class-resolver';
interface StripeEvent {
type: string;
data: { amount: number };
}
class AccountingHandler implements ResolveTarget<[StripeEvent], string, StripeEvent> {
supports(event: StripeEvent): boolean {
return event.type === 'payment.succeeded';
}
handle(event: StripeEvent): string {
return `Accounting: Recorded ${event.data.amount}`;
}
}
class EmailHandler implements ResolveTarget<[StripeEvent], string, StripeEvent> {
supports(event: StripeEvent): boolean {
return event.type === 'payment.succeeded';
}
handle(event: StripeEvent): string {
return `Email: Sent confirmation for ${event.data.amount}`;
}
}
const resolver = new Resolver<ResolveTarget<[StripeEvent], string, StripeEvent>, StripeEvent>(
new AccountingHandler(),
new EmailHandler()
);
const event: StripeEvent = {
type: 'payment.succeeded',
data: { amount: 1000 }
};
// Execute ALL matching handlers
const results = resolver.handleAll(event, event);
// Results: ['Accounting: Recorded 1000', 'Email: Sent confirmation for 1000']
// Or get all matching handlers
const handlers = resolver.resolveAll(event);
// handlers.length === 2New Methods:
resolveAll(type): Returns all matching handlers sorted by priorityhandleAll(type, ...args): Executes all matching handlers and returns their results
2. Priority-Based Handler Resolution
Control execution order with priority levels. Higher priority handlers execute first.
import { PrioritizedResolveTarget } from 'class-resolver';
class ValidationHandler implements PrioritizedResolveTarget<[any], boolean, string> {
priority = 100; // Highest priority
supports(type: string): boolean {
return type === 'webhook';
}
handle(data: any): boolean {
return data !== null && data !== undefined;
}
}
class BusinessLogicHandler implements PrioritizedResolveTarget<[any], string, string> {
priority = 50; // Medium priority
supports(type: string): boolean {
return type === 'webhook';
}
handle(data: any): string {
return `Processed: ${JSON.stringify(data)}`;
}
}
const resolver = new Resolver<PrioritizedResolveTarget<[any], any, string>, string>(
new BusinessLogicHandler(), // Registered second
new ValidationHandler() // Registered first
);
// Handlers execute in PRIORITY order (not registration order):
// 1. ValidationHandler (priority: 100)
// 2. BusinessLogicHandler (priority: 50)
const results = resolver.handleAll('webhook', { test: true });New Interface:
PrioritizedResolveTarget<TArgs, TReturn, TType>: ExtendsResolveTargetwith optionalpriorityproperty
3. Async Handler Support
Execute async handlers in parallel or sequentially.
import { AsyncResolveTarget } from 'class-resolver';
class SaveToDBHandler implements AsyncResolveTarget<[any], string, string> {
supports(type: string): boolean {
return type === 'payment';
}
async handle(data: any): Promise<string> {
await new Promise(resolve => setTimeout(resolve, 100));
return 'Saved to DB';
}
}
class SendWebhookHandler implements AsyncResolveTarget<[any], string, string> {
supports(type: string): boolean {
return type === 'payment';
}
async handle(data: any): Promise<string> {
await new Promise(resolve => setTimeout(resolve, 200));
return 'Webhook sent';
}
}
const resolver = new Resolver<AsyncResolveTarget<[any], string, string>, string>(
new SaveToDBHandler(),
new SendWebhookHandler()
);
// Execute handlers in PARALLEL (fastest)
const results = await resolver.handleAllAsync('payment', { amount: 1000 });
// Results: ['Saved to DB', 'Webhook sent']
// Total time: ~200ms (not 300ms)
// Or execute SEQUENTIALLY (ordered, stops on error)
const results2 = await resolver.handleAllSequential('payment', { amount: 1000 });
// Results: ['Saved to DB', 'Webhook sent']
// Total time: ~300msNew Methods:
handleAllAsync(type, ...args): Executes all matching async handlers in parallelhandleAllSequential(type, ...args): Executes all matching async handlers sequentially (stops on first error)
New Interfaces:
AsyncResolveTarget<TArgs, TReturn, TType>: For async handlersPrioritizedAsyncResolveTarget<TArgs, TReturn, TType>: Combines async support with priority
4. Priority + Async Combined
You can combine priority and async support for powerful event processing pipelines:
import { PrioritizedAsyncResolveTarget } from 'class-resolver';
class ValidationHandler implements PrioritizedAsyncResolveTarget<[any], boolean, string> {
priority = 100;
supports(type: string): boolean {
return type === 'order';
}
async handle(data: any): Promise<boolean> {
return data.amount > 0;
}
}
class ProcessHandler implements PrioritizedAsyncResolveTarget<[any], string, string> {
priority = 50;
supports(type: string): boolean {
return type === 'order';
}
async handle(data: any): Promise<string> {
return `Processed order ${data.id}`;
}
}
const resolver = new Resolver<PrioritizedAsyncResolveTarget<[any], any, string>, string>(
new ProcessHandler(), // priority: 50
new ValidationHandler() // priority: 100
);
// Executes in priority order: Validation → Process
const results = await resolver.handleAllAsync('order', { id: 123, amount: 1000 });
// Results: [true, 'Processed order 123']🎯 Use Cases
- Webhook Fanout: Process a single webhook event with multiple handlers (accounting, notifications, analytics)
- Event-Driven Architecture: Route events to multiple subscribers based on event type
- Validation Pipeline: Execute validation, business logic, and logging in priority order
- Async Workflows: Coordinate multiple async operations (DB saves, API calls, file operations)
- Plugin System: Implement a plugin system where different plugins handle specific types of operations
📦 Installation
npm install class-resolver@^4.0.0
# or
yarn add class-resolver@^4.0.0📚 Documentation
Full documentation is available in the README.md.
🔗 Links
🙏 Thanks
Thank you to all contributors and users who have helped make this release possible!