0
# Notification Service
1
2
Interface for notification service implementations enabling sending and resending notifications through various channels like email, SMS, or push notifications.
3
4
**Deprecation Notice**: Use `AbstractNotificationService` from @medusajs/medusa instead.
5
6
## Capabilities
7
8
### Static Methods
9
10
Type checking and identification methods for notification services.
11
12
```javascript { .api }
13
/**
14
* Static property identifying this as a notification service
15
*/
16
static _isNotificationService: boolean;
17
18
/**
19
* Checks if an object is a notification service
20
* @param {object} obj - Object to check
21
* @returns {boolean} True if obj is a notification service
22
*/
23
static isNotificationService(obj: object): boolean;
24
```
25
26
### Service Identification
27
28
Method to get the service identifier.
29
30
```javascript { .api }
31
/**
32
* Returns the service identifier from constructor
33
* @returns {string} Service identifier
34
*/
35
getIdentifier(): string;
36
```
37
38
### Core Notification Operations
39
40
Abstract methods that must be implemented by child classes.
41
42
```javascript { .api }
43
/**
44
* Sends a notification for a specific event with provided data
45
* @param {string} event - The event type that triggered the notification
46
* @param {object} data - Data associated with the event and notification
47
* @returns {any} Notification sending result
48
* @throws {Error} If not overridden by child class
49
*/
50
sendNotification(event: string, data: object): any;
51
52
/**
53
* Resends an existing notification with optional configuration
54
* @param {object} notification - The notification object to resend
55
* @param {object} config - Optional configuration for resending
56
* @returns {any} Notification resending result
57
* @throws {Error} If not overridden by child class
58
*/
59
resendNotification(notification: object, config?: object): any;
60
```
61
62
## Implementation Example
63
64
```javascript
65
import { NotificationService } from "medusa-interfaces";
66
67
class EmailNotificationService extends NotificationService {
68
static identifier = "email-notification";
69
70
constructor(options) {
71
super();
72
this.emailClient = options.email_client;
73
this.templates = options.templates || {};
74
this.from_email = options.from_email;
75
}
76
77
async sendNotification(event, data) {
78
// Map events to email templates
79
const template = this.getTemplateForEvent(event);
80
if (!template) {
81
throw new Error(`No template found for event: ${event}`);
82
}
83
84
const emailData = this.prepareEmailData(event, data, template);
85
86
try {
87
const result = await this.emailClient.send({
88
from: this.from_email,
89
to: emailData.to,
90
subject: emailData.subject,
91
html: emailData.html,
92
text: emailData.text
93
});
94
95
return {
96
id: result.messageId,
97
event: event,
98
recipient: emailData.to,
99
status: "sent",
100
sent_at: new Date()
101
};
102
} catch (error) {
103
throw new Error(`Failed to send notification: ${error.message}`);
104
}
105
}
106
107
async resendNotification(notification, config = {}) {
108
// Override recipient if provided in config
109
const recipient = config.recipient || notification.recipient;
110
111
try {
112
const result = await this.emailClient.send({
113
from: this.from_email,
114
to: recipient,
115
subject: notification.subject,
116
html: notification.html,
117
text: notification.text
118
});
119
120
return {
121
id: result.messageId,
122
original_id: notification.id,
123
event: notification.event,
124
recipient: recipient,
125
status: "resent",
126
sent_at: new Date()
127
};
128
} catch (error) {
129
throw new Error(`Failed to resend notification: ${error.message}`);
130
}
131
}
132
133
getTemplateForEvent(event) {
134
const eventTemplateMap = {
135
"order.placed": "order_confirmation",
136
"order.shipped": "order_shipped",
137
"order.cancelled": "order_cancelled",
138
"customer.password_reset": "password_reset",
139
"customer.created": "welcome_email"
140
};
141
142
return this.templates[eventTemplateMap[event]];
143
}
144
145
prepareEmailData(event, data, template) {
146
switch (event) {
147
case "order.placed":
148
return {
149
to: data.order.email,
150
subject: `Order Confirmation - #${data.order.display_id}`,
151
html: template.renderHtml(data),
152
text: template.renderText(data)
153
};
154
155
case "order.shipped":
156
return {
157
to: data.order.email,
158
subject: `Your order has shipped - #${data.order.display_id}`,
159
html: template.renderHtml(data),
160
text: template.renderText(data)
161
};
162
163
case "customer.password_reset":
164
return {
165
to: data.customer.email,
166
subject: "Password Reset Request",
167
html: template.renderHtml(data),
168
text: template.renderText(data)
169
};
170
171
default:
172
throw new Error(`Unsupported event: ${event}`);
173
}
174
}
175
}
176
177
// SMS Notification Service Example
178
class SMSNotificationService extends NotificationService {
179
static identifier = "sms-notification";
180
181
constructor(options) {
182
super();
183
this.smsClient = options.sms_client;
184
this.from_number = options.from_number;
185
}
186
187
async sendNotification(event, data) {
188
const message = this.getMessageForEvent(event, data);
189
const recipient = this.getRecipientPhoneNumber(data);
190
191
if (!recipient) {
192
throw new Error("No phone number found for SMS notification");
193
}
194
195
try {
196
const result = await this.smsClient.messages.create({
197
body: message,
198
from: this.from_number,
199
to: recipient
200
});
201
202
return {
203
id: result.sid,
204
event: event,
205
recipient: recipient,
206
status: "sent",
207
sent_at: new Date()
208
};
209
} catch (error) {
210
throw new Error(`Failed to send SMS: ${error.message}`);
211
}
212
}
213
214
async resendNotification(notification, config = {}) {
215
const recipient = config.recipient || notification.recipient;
216
217
try {
218
const result = await this.smsClient.messages.create({
219
body: notification.message,
220
from: this.from_number,
221
to: recipient
222
});
223
224
return {
225
id: result.sid,
226
original_id: notification.id,
227
event: notification.event,
228
recipient: recipient,
229
status: "resent",
230
sent_at: new Date()
231
};
232
} catch (error) {
233
throw new Error(`Failed to resend SMS: ${error.message}`);
234
}
235
}
236
237
getMessageForEvent(event, data) {
238
switch (event) {
239
case "order.placed":
240
return `Order confirmed! Your order #${data.order.display_id} has been placed.`;
241
242
case "order.shipped":
243
return `Your order #${data.order.display_id} has shipped! Track: ${data.tracking_url}`;
244
245
default:
246
return `Notification for event: ${event}`;
247
}
248
}
249
250
getRecipientPhoneNumber(data) {
251
return data.order?.phone || data.customer?.phone || data.phone;
252
}
253
}
254
```
255
256
## Usage in Medusa
257
258
Notification services are typically used for:
259
260
- **Order Notifications**: Confirmation, shipping, cancellation
261
- **Customer Communications**: Welcome emails, password resets
262
- **Admin Alerts**: Low inventory, failed payments
263
- **Marketing**: Promotional campaigns, newsletters
264
265
**Basic Usage Pattern:**
266
267
```javascript
268
// In a Medusa service or subscriber
269
class OrderService {
270
constructor({ notificationService }) {
271
this.notificationService_ = notificationService;
272
}
273
274
async placeOrder(order) {
275
// ... order placement logic
276
277
// Send order confirmation notification
278
await this.notificationService_.sendNotification("order.placed", {
279
order: order,
280
customer: order.customer,
281
items: order.items
282
});
283
284
return order;
285
}
286
}
287
288
// Event subscriber example
289
class OrderSubscriber {
290
constructor({ notificationService }) {
291
this.notificationService_ = notificationService;
292
}
293
294
async handleOrderPlaced(data) {
295
await this.notificationService_.sendNotification("order.placed", data);
296
}
297
298
async handleOrderShipped(data) {
299
await this.notificationService_.sendNotification("order.shipped", data);
300
}
301
}
302
```
303
304
## Multi-Channel Implementation
305
306
```javascript
307
class MultiChannelNotificationService extends NotificationService {
308
static identifier = "multi-channel";
309
310
constructor(options) {
311
super();
312
this.emailService = options.email_service;
313
this.smsService = options.sms_service;
314
this.pushService = options.push_service;
315
}
316
317
async sendNotification(event, data) {
318
const results = [];
319
const channels = this.getChannelsForEvent(event, data);
320
321
for (const channel of channels) {
322
try {
323
let result;
324
switch (channel) {
325
case "email":
326
result = await this.emailService.sendNotification(event, data);
327
break;
328
case "sms":
329
result = await this.smsService.sendNotification(event, data);
330
break;
331
case "push":
332
result = await this.pushService.sendNotification(event, data);
333
break;
334
}
335
336
results.push({ channel, ...result });
337
} catch (error) {
338
results.push({
339
channel,
340
status: "failed",
341
error: error.message
342
});
343
}
344
}
345
346
return results;
347
}
348
349
getChannelsForEvent(event, data) {
350
// Determine which channels to use based on event and user preferences
351
const defaultChannels = ["email"];
352
353
if (data.customer?.notification_preferences) {
354
return data.customer.notification_preferences[event] || defaultChannels;
355
}
356
357
return defaultChannels;
358
}
359
}
360
```
361
362
## Error Handling
363
364
Both abstract methods throw descriptive errors when not implemented:
365
366
- `"Must be overridden by child"` (for `sendNotification`)
367
- `"Must be overridden by child"` (for `resendNotification`)