0
# Events System
1
2
Sails.js inherits from Node.js EventEmitter and provides a comprehensive event system for managing application lifecycle, routing events, and custom functionality. The event system enables loose coupling between components and supports both built-in framework events and custom application events.
3
4
## EventEmitter Inheritance
5
6
Sails applications inherit from Node.js EventEmitter, providing full event functionality:
7
8
```javascript { .api }
9
// Sails extends EventEmitter
10
class Sails extends EventEmitter { ... }
11
12
// Event listener methods
13
sails.on(event: String, listener: Function): Sails
14
sails.once(event: String, listener: Function): Sails
15
sails.off(event: String, listener?: Function): Sails
16
sails.emit(event: String, ...args): Boolean
17
sails.removeAllListeners(event?: String): Sails
18
sails.listenerCount(event: String): Number
19
sails.listeners(event: String): Function[]
20
```
21
22
**Basic Event Usage**:
23
```javascript
24
const sails = require('sails');
25
26
// Listen for events
27
sails.on('lifted', () => {
28
console.log('Sails app is ready!');
29
});
30
31
// Listen once
32
sails.once('ready', () => {
33
console.log('App components loaded');
34
});
35
36
// Remove listener
37
const handler = () => console.log('Event fired');
38
sails.on('custom-event', handler);
39
sails.off('custom-event', handler);
40
41
// Emit custom events
42
sails.emit('user-registered', { userId: 123, email: 'user@example.com' });
43
```
44
45
## Core Lifecycle Events
46
47
### Application Lifecycle Events
48
49
```javascript { .api }
50
// Application successfully lifted
51
sails.on('lifted', callback: Function): void
52
53
// Application shutdown initiated
54
sails.on('lower', callback: Function): void
55
56
// Application components loaded (internal)
57
sails.on('ready', callback: Function): void
58
```
59
60
**Lifecycle Event Examples**:
61
```javascript
62
// Application startup complete
63
sails.on('lifted', () => {
64
console.log(`Sails app lifted on port ${sails.config.port}`);
65
console.log(`Environment: ${sails.config.environment}`);
66
67
// Start background jobs
68
startBackgroundTasks();
69
70
// Register with service discovery
71
registerWithLoadBalancer();
72
});
73
74
// Application shutdown initiated
75
sails.on('lower', () => {
76
console.log('Sails app is shutting down...');
77
78
// Cleanup background jobs
79
stopBackgroundTasks();
80
81
// Close external connections
82
closeExternalConnections();
83
});
84
85
// Internal ready state (before HTTP server starts)
86
sails.on('ready', () => {
87
console.log('App components loaded, starting HTTP server...');
88
89
// Perform pre-startup tasks
90
validateConfiguration();
91
initializeServices();
92
});
93
```
94
95
### Startup Sequence Events
96
97
The application startup follows a specific event sequence:
98
99
```javascript
100
// 1. Configuration loaded
101
sails.on('hook:userconfig:loaded', () => {
102
console.log('User configuration loaded');
103
});
104
105
// 2. Hooks initialized
106
sails.on('hook:*:loaded', (hookName) => {
107
console.log(`Hook loaded: ${hookName}`);
108
});
109
110
// 3. Components ready
111
sails.on('ready', () => {
112
console.log('All components ready');
113
});
114
115
// 4. Application lifted
116
sails.on('lifted', () => {
117
console.log('Application fully operational');
118
});
119
```
120
121
## Router Events
122
123
### Route Binding Events
124
125
```javascript { .api }
126
// Before static routes are bound
127
sails.on('router:before', callback: Function): void
128
129
// After static routes are bound
130
sails.on('router:after', callback: Function): void
131
132
// Router reset event
133
sails.on('router:reset', callback: Function): void
134
```
135
136
**Router Event Examples**:
137
```javascript
138
// Before route binding - modify routes
139
sails.on('router:before', () => {
140
console.log('Binding routes...');
141
142
// Add dynamic routes
143
sails.get('/health', (req, res) => {
144
return res.json({ status: 'ok', timestamp: Date.now() });
145
});
146
147
// Modify existing routes
148
modifyApiRoutes();
149
});
150
151
// After route binding - log route information
152
sails.on('router:after', () => {
153
console.log('Routes bound successfully');
154
155
const routes = sails.router.getSortedRouteAddresses();
156
console.log(`Total routes: ${routes.length}`);
157
158
// Log all routes in development
159
if (sails.config.environment === 'development') {
160
routes.forEach(route => console.log(` ${route}`));
161
}
162
});
163
164
// Router reset - cleanup custom routes
165
sails.on('router:reset', () => {
166
console.log('Router reset, clearing custom routes');
167
clearCustomRoutes();
168
});
169
```
170
171
### Request Handling Events
172
173
```javascript { .api }
174
// Virtual request routing
175
sails.on('router:request', (req, res): void
176
177
// Server error handling
178
sails.on('router:request:500', (req, res, err): void
179
180
// Not found handling
181
sails.on('router:request:404', (req, res): void
182
```
183
184
**Request Event Examples**:
185
```javascript
186
// Log all virtual requests
187
sails.on('router:request', (req, res) => {
188
console.log(`Virtual request: ${req.method} ${req.url}`);
189
});
190
191
// Custom 500 error handling
192
sails.on('router:request:500', (req, res, err) => {
193
console.error('Server error:', err);
194
195
// Log error details
196
console.error(`URL: ${req.url}`);
197
console.error(`Method: ${req.method}`);
198
console.error(`User: ${req.session?.userId || 'anonymous'}`);
199
200
// Send custom error response
201
if (!res.headersSent) {
202
return res.status(500).json({
203
error: 'Internal server error',
204
requestId: req.id,
205
timestamp: Date.now()
206
});
207
}
208
});
209
210
// Custom 404 handling
211
sails.on('router:request:404', (req, res) => {
212
console.log(`404 Not Found: ${req.method} ${req.url}`);
213
214
// Track 404s for analytics
215
trackNotFoundRequest(req);
216
217
// Send custom 404 response
218
if (!res.headersSent) {
219
return res.status(404).json({
220
error: 'Resource not found',
221
path: req.url,
222
suggestion: 'Check the API documentation'
223
});
224
}
225
});
226
```
227
228
## Hook Events
229
230
Hooks can define and emit custom events during their lifecycle:
231
232
### Hook Lifecycle Events
233
234
```javascript { .api }
235
// Hook-specific events
236
sails.on('hook:${hookName}:loaded', callback: Function): void
237
sails.on('hook:${hookName}:ready', callback: Function): void
238
```
239
240
**Hook Event Examples**:
241
```javascript
242
// Listen for specific hook events
243
sails.on('hook:orm:loaded', () => {
244
console.log('ORM hook loaded, models available');
245
246
// Perform database initialization
247
initializeDatabase();
248
});
249
250
sails.on('hook:sockets:loaded', () => {
251
console.log('Sockets hook loaded, WebSocket support available');
252
253
// Configure real-time features
254
setupRealtimeFeatures();
255
});
256
257
// Listen for all hook load events
258
sails.on('hook:*:loaded', (hookName) => {
259
console.log(`Hook loaded: ${hookName}`);
260
261
// Track loading progress
262
trackHookLoadingProgress(hookName);
263
});
264
```
265
266
### Custom Hook Events
267
268
Custom hooks can emit their own events:
269
270
```javascript
271
// api/hooks/my-hook/index.js
272
module.exports = function(sails) {
273
return {
274
initialize: function(cb) {
275
// Emit custom hook event
276
sails.emit('hook:my-hook:initialized', {
277
message: 'My hook is ready',
278
features: ['feature1', 'feature2']
279
});
280
281
// Set up periodic events
282
setInterval(() => {
283
sails.emit('hook:my-hook:heartbeat', { timestamp: Date.now() });
284
}, 30000);
285
286
return cb();
287
}
288
};
289
};
290
291
// Listen for custom hook events
292
sails.on('hook:my-hook:initialized', (data) => {
293
console.log('Custom hook initialized:', data.message);
294
console.log('Available features:', data.features);
295
});
296
297
sails.on('hook:my-hook:heartbeat', (data) => {
298
console.log('Hook heartbeat:', new Date(data.timestamp));
299
});
300
```
301
302
## Custom Application Events
303
304
Applications can define and emit custom events for business logic:
305
306
### User Events
307
308
```javascript
309
// User registration event
310
sails.on('user:registered', (userData) => {
311
console.log('New user registered:', userData.email);
312
313
// Send welcome email
314
EmailService.sendWelcomeEmail(userData.email, userData.name);
315
316
// Create user profile
317
UserProfileService.createProfile(userData.id);
318
319
// Track registration
320
AnalyticsService.track('user_registered', {
321
userId: userData.id,
322
source: userData.registrationSource
323
});
324
});
325
326
// User login event
327
sails.on('user:login', (loginData) => {
328
console.log('User logged in:', loginData.userId);
329
330
// Update last login timestamp
331
User.updateOne(loginData.userId).set({ lastLoginAt: Date.now() });
332
333
// Log security events
334
SecurityService.logLogin(loginData);
335
});
336
337
// Emit user events
338
const newUser = await User.create({
339
email: 'user@example.com',
340
name: 'John Doe'
341
}).fetch();
342
343
sails.emit('user:registered', {
344
id: newUser.id,
345
email: newUser.email,
346
name: newUser.name,
347
registrationSource: 'web'
348
});
349
```
350
351
### Business Logic Events
352
353
```javascript
354
// Order events
355
sails.on('order:created', async (orderData) => {
356
console.log('New order created:', orderData.id);
357
358
// Send confirmation email
359
await EmailService.sendOrderConfirmation(orderData);
360
361
// Update inventory
362
await InventoryService.reserveItems(orderData.items);
363
364
// Process payment
365
await PaymentService.processPayment(orderData.paymentInfo);
366
});
367
368
sails.on('order:shipped', (orderData) => {
369
console.log('Order shipped:', orderData.id);
370
371
// Send shipping notification
372
NotificationService.sendShippingNotification(orderData);
373
374
// Update tracking
375
TrackingService.createTrackingRecord(orderData);
376
});
377
378
// Payment events
379
sails.on('payment:successful', (paymentData) => {
380
console.log('Payment successful:', paymentData.transactionId);
381
382
// Fulfill order
383
OrderService.fulfillOrder(paymentData.orderId);
384
385
// Send receipt
386
EmailService.sendReceipt(paymentData);
387
});
388
389
sails.on('payment:failed', (paymentData) => {
390
console.error('Payment failed:', paymentData.error);
391
392
// Cancel order
393
OrderService.cancelOrder(paymentData.orderId);
394
395
// Notify customer
396
NotificationService.sendPaymentFailureNotification(paymentData);
397
});
398
```
399
400
## Event-Driven Architecture Patterns
401
402
### Event Aggregation
403
404
```javascript
405
// Collect multiple related events
406
const EventAggregator = {
407
events: [],
408
409
collect(eventName, eventData) {
410
this.events.push({ name: eventName, data: eventData, timestamp: Date.now() });
411
},
412
413
flush() {
414
const events = this.events.slice();
415
this.events = [];
416
return events;
417
}
418
};
419
420
// Listen to multiple events
421
['user:registered', 'user:login', 'user:logout'].forEach(eventName => {
422
sails.on(eventName, (data) => {
423
EventAggregator.collect(eventName, data);
424
});
425
});
426
427
// Periodic processing
428
setInterval(() => {
429
const events = EventAggregator.flush();
430
if (events.length > 0) {
431
AnalyticsService.batchProcess(events);
432
}
433
}, 60000); // Every minute
434
```
435
436
### Event Middleware
437
438
```javascript
439
// Event middleware pattern
440
const EventMiddleware = {
441
use(eventName, middleware) {
442
const originalEmit = sails.emit;
443
444
sails.emit = function(event, ...args) {
445
if (event === eventName) {
446
// Run middleware before emitting
447
middleware(event, args, () => {
448
originalEmit.call(this, event, ...args);
449
});
450
} else {
451
originalEmit.call(this, event, ...args);
452
}
453
};
454
}
455
};
456
457
// Use middleware
458
EventMiddleware.use('user:registered', (event, args, next) => {
459
console.log('Middleware: Processing user registration');
460
461
// Validate event data
462
if (!args[0].email) {
463
console.error('Invalid user registration data');
464
return; // Don't proceed
465
}
466
467
// Add metadata
468
args[0].processedAt = Date.now();
469
470
next();
471
});
472
```
473
474
### Event Sourcing Pattern
475
476
```javascript
477
// Simple event sourcing implementation
478
const EventStore = {
479
events: [],
480
481
append(event) {
482
this.events.push({
483
...event,
484
id: require('uuid').v4(),
485
timestamp: Date.now()
486
});
487
},
488
489
getEvents(aggregateId) {
490
return this.events.filter(e => e.aggregateId === aggregateId);
491
},
492
493
replay(aggregateId) {
494
const events = this.getEvents(aggregateId);
495
return events.reduce((state, event) => {
496
return applyEvent(state, event);
497
}, {});
498
}
499
};
500
501
// Store all user events
502
sails.on('user:*', (eventData) => {
503
EventStore.append({
504
type: 'user_event',
505
aggregateId: eventData.userId,
506
data: eventData
507
});
508
});
509
```
510
511
## Error Handling with Events
512
513
### Event Error Handling
514
515
```javascript
516
// Handle event errors
517
sails.on('error', (err) => {
518
console.error('Unhandled event error:', err);
519
520
// Log to external service
521
ErrorReportingService.reportError(err);
522
});
523
524
// Safe event emission
525
function safeEmit(eventName, eventData) {
526
try {
527
sails.emit(eventName, eventData);
528
} catch (err) {
529
console.error(`Error emitting event ${eventName}:`, err);
530
sails.emit('event:error', { eventName, eventData, error: err });
531
}
532
}
533
534
// Error recovery
535
sails.on('event:error', ({ eventName, eventData, error }) => {
536
console.log(`Attempting to recover from event error: ${eventName}`);
537
538
// Retry logic
539
setTimeout(() => {
540
safeEmit(eventName, eventData);
541
}, 5000);
542
});
543
```
544
545
## Event Performance and Monitoring
546
547
### Event Metrics
548
549
```javascript
550
// Event performance monitoring
551
const EventMetrics = {
552
counts: new Map(),
553
timings: new Map(),
554
555
track(eventName) {
556
this.counts.set(eventName, (this.counts.get(eventName) || 0) + 1);
557
},
558
559
time(eventName, duration) {
560
if (!this.timings.has(eventName)) {
561
this.timings.set(eventName, []);
562
}
563
this.timings.get(eventName).push(duration);
564
},
565
566
getStats() {
567
const stats = {};
568
569
for (const [event, count] of this.counts) {
570
stats[event] = { count };
571
572
if (this.timings.has(event)) {
573
const times = this.timings.get(event);
574
stats[event].avgTime = times.reduce((a, b) => a + b, 0) / times.length;
575
}
576
}
577
578
return stats;
579
}
580
};
581
582
// Monitor all events
583
const originalEmit = sails.emit;
584
sails.emit = function(event, ...args) {
585
const start = Date.now();
586
EventMetrics.track(event);
587
588
const result = originalEmit.call(this, event, ...args);
589
590
EventMetrics.time(event, Date.now() - start);
591
return result;
592
};
593
594
// Log metrics periodically
595
setInterval(() => {
596
console.log('Event Statistics:', EventMetrics.getStats());
597
}, 300000); // Every 5 minutes
598
```
599
600
The Sails.js event system provides a robust foundation for building event-driven applications with comprehensive lifecycle management, routing events, and custom business logic events, supporting modern architectural patterns and real-time functionality.