Advanced
Event System
Build reactive applications with MoroJS's powerful event system. Decouple components, enable real-time features, and create scalable architectures.
Overview
Emit and listen to events with one line. Decouple components, enable real-time features, and build reactive architectures.
1import { createApp } from '@morojs/moro';
2
3const app = await createApp();
4const events = app.events;
5
6// Emit events from route handlers
7app.post('/users')
8 .body(z.object({
9 email: z.string().email(),
10 name: z.string()
11 }))
12 .handler(async (req, res) => {
13 const user = await createUser(req.body);
14
15 // Emit user creation event
16 await events.emit('user.created', {
17 user,
18 timestamp: new Date().toISOString()
19 });
20
21 res.json(user);
22 });
23
24// Listeners receive { context, data }
25events.on('user.created', async ({ data }) => {
26 console.log('New user created:', data.user.email);
27 await sendWelcomeEmail(data.user.email);
28 await trackUserSignup(data.user);
29});Without proper event systems, you're tightly coupling components, blocking requests, and making real-time features difficult. With MoroJS, you get all of that automatically. Traditional event handling requires manual setup and coordination. We handle that automatically.
Without Events
- Tightly coupled components
- Blocking request handlers
- No real-time capabilities
- Hard to scale and maintain
With MoroJS
- Decoupled component architecture
- Asynchronous event processing
- Built-in real-time support
- Scalable reactive patterns
1// Emit events
2await events.emit('user.created', { user });
3await events.emit('order.completed', { order });
4
5// Listeners receive an EventPayload — { context, data } — so destructure
6// 'data' to reach the value you emitted.
7events.on('user.created', async ({ data }) => {
8 await sendWelcomeEmail(data.user);
9});
10
11events.on('order.completed', async ({ data }) => {
12 await sendConfirmationEmail(data.order);
13});
14
15// One-time listeners
16events.once('app.started', () => {
17 console.log('App started!');
18});Decoupled
Components communicate through events. No direct dependencies.
Reactive
Build reactive applications with event-driven architecture.
Scalable
Easy to add new listeners. Easy to scale. Easy to maintain.
How It Works
MoroJS includes a built-in event bus that allows components to communicate asynchronously. You emit events from route handlers, modules, or anywhere in your application, and listeners react to those events. This decouples components and enables reactive, scalable architectures.
Basic Event Usage
1import { createApp } from '@morojs/moro';
2
3const app = await createApp();
4const events = app.events;
5
6// Emit events from route handlers
7app.post('/users')
8 .body(z.object({
9 email: z.string().email(),
10 name: z.string()
11 }))
12 .handler(async (req, res) => {
13 const user = await createUser(req.body);
14
15 // Emit user creation event
16 await events.emit('user.created', {
17 user,
18 timestamp: new Date().toISOString()
19 });
20
21 res.json(user);
22 });
23
24// Listeners receive { context, data }
25events.on('user.created', async ({ data }) => {
26 console.log('New user created:', data.user.email);
27
28 // Send welcome email
29 await sendWelcomeEmail(data.user.email);
30
31 // Log to analytics
32 await trackUserSignup(data.user);
33});
34
35// One-time event listeners
36events.once('app.started', () => {
37 console.log('Application has started successfully');
38});
39
40// Remove event listeners
41const handler = ({ data }) => console.log('User updated:', data);
42events.on('user.updated', handler);
43events.off('user.updated', handler);Event System Benefits
- Decoupled component architecture
- Asynchronous processing capabilities
- Real-time feature support
- Module isolation and communication
- Scalable reactive patterns
Advanced Event Patterns
1// Functional Event Handling
2export function createUserEventHandlers(services: any) {
3 return {
4 async handleUserCreated(data: { user: User }) {
5 console.log('Handling user creation:', data.user.id);
6
7 // Send welcome email
8 if (services.emailService) {
9 await services.emailService.sendWelcomeEmail(data.user);
10 }
11
12 // Create user profile
13 if (services.profileService) {
14 await services.profileService.createUserProfile(data.user);
15 }
16
17 // Track analytics
18 if (services.analyticsService) {
19 await services.analyticsService.trackSignup(data.user);
20 }
21 },
22
23 async handleUserUpdated(data: { user: User, changes: Partial<User> }) {
24 console.log('User updated:', data.user.id, data.changes);
25
26 // Invalidate cache
27 if (services.cacheService) {
28 await services.cacheService.invalidateUserCache(data.user.id);
29 }
30
31 // Notify connected clients
32 if (services.notificationService) {
33 await services.notificationService.notifyUserUpdate(data.user);
34 }
35 },
36
37 async handleUserDeleted(data: { userId: string }) {
38 console.log('User deleted:', data.userId);
39
40 // Clean up user data
41 if (services.cleanupService) {
42 await services.cleanupService.cleanupUserData(data.userId);
43 }
44
45 // Archive user information
46 if (services.archiveService) {
47 await services.archiveService.archiveUser(data.userId);
48 }
49 }
50 };
51}1import { createApp, createFrameworkLogger } from '@morojs/moro';
2
3const app = await createApp();
4const events = app.events;
5const log = createFrameworkLogger('Events');
6
7// The bus has no middleware chain. For cross-cutting concerns, wrap emit:
8const emit = async (name, data) => {
9 log.debug('Event emitted', 'Bus', { name });
10 return events.emit(name, data);
11};
12
13// Built-in introspection
14events.enableAuditLog();
15const recent = events.getAuditLog(); // recent emissions
16const metrics = events.getMetrics(); // counts and timings
17
18// Per-module buses keep namespaces isolated
19const userBus = events.createModuleBus('users');
20userBus.on('created', ({ data }) => sendWelcomeEmail(data.user));
21await userBus.emit('created', { user });
22
23// Background work: schedule it with app.job (requires jobs.enabled)
24app.job('rebuild-recommendations', '15m', async () => {
25 await generateUserRecommendations();
26});