or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

base-service.mdfile-service.mdfulfillment-service.mdindex.mdnotification-service.mdoauth-service.mdpayment-service.mdsearch-service.md

notification-service.mddocs/

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`)