Generated the following references: - `api_key_models` - `auth_models` - `cart_models` - `core_flows` - `currency_models` - `customer_models` - `file` - `file_service` - `fulfillment` - `fulfillment_models` - `helper_steps` - `inventory_next_models` - `js_sdk` - `module_events` - `modules` - `notification` - `notification_service` - `order` - `order_models` - `payment` - `payment_models` - `pricing_models` - `product_models` - `promotion` - `promotion_models` - `region_models` - `sales_channel_models` - `stock_location_next_models` - `store_models` - `tax_models` - `types` - `user_models` - `utils` - `workflows` --- > [!NOTE] > Regenerates documentation references with updated schemas/fields and source links, adds locking guidance to fulfillment delivery workflow, expands notification step IO, and tweaks examples/sample values. > > - **Docs Generation**: > - Regenerated reference pages across `core_flows`, `fulfillment`, `inventory`, `order`, etc., with updated TypeList schemas and examples. > - **Fulfillment Workflows/Steps**: > - Updated IO schemas (e.g., added `custom_display_id` on `OrderDTO`, `carry_over_promotions` on `OrderChangeDTO`). > - Added locking guidance and workflow steps (`acquireLockStep`/`releaseLockStep`) to `markFulfillmentAsDeliveredWorkflow` plus new note/tag. > - Refreshed example snippets and sample values. > - **Notification Step** (`sendNotificationsStep`): > - Input expanded with `from`, `content`, `provider_data`, `attachments`; `template` now optional. > - Output includes `provider_data` and `attachments` fields. > - **Order Steps/Workflows**: > - IO schemas updated to include `custom_display_id` and `carry_over_promotions` where applicable; added `ordering` on change actions. > - Example payloads adjusted (IDs, currencies, amounts). > - **Meta**: > - Updated `<SourceCodeLink>` URLs to new commit hashes throughout. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit eeb0dac7b19d51860531a491208b2b3b853aa4db. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> Co-authored-by: Shahed Nasser <27354907+shahednasser@users.noreply.github.com>
313 lines
11 KiB
Plaintext
313 lines
11 KiB
Plaintext
---
|
||
slug: /references/notification-provider-module
|
||
tags:
|
||
- notification
|
||
- server
|
||
- how to
|
||
sidebar_label: Create Notification Provider
|
||
---
|
||
|
||
import { TypeList } from "docs-ui"
|
||
|
||
# How to Create a Notification Module Provider
|
||
|
||
In this document, you’ll learn how to create a Notification Module Provider and the methods you must implement in it.
|
||
|
||
---
|
||
|
||
## Implementation Example
|
||
|
||
As you implement your Notification Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||
|
||
If you need to refer to an existing implementation as an example, check the [SendGrid Notification Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/notification-sendgrid).
|
||
|
||
---
|
||
|
||
## 1. Create Module Provider Directory
|
||
|
||
Start by creating a new directory for your module provider.
|
||
|
||
If you're creating the module provider in a Medusa application, create it under the `src/modules` directory. For example, `src/modules/my-notification`.
|
||
|
||
If you're creating the module provider in a plugin, create it under the `src/providers` directory. For example, `src/providers/my-notification`.
|
||
|
||
<Note>
|
||
|
||
The rest of this guide always uses the `src/modules/my-notification` directory as an example.
|
||
|
||
</Note>
|
||
|
||
---
|
||
|
||
## 2. Create the Notification Module Provider's Service
|
||
|
||
Create the file `src/modules/my-notification/service.ts` that holds the implementation of the notification service.
|
||
|
||
The Notification Module Provider's main service must extend the `AbstractNotificationProviderService` class imported from `@medusajs/framework/utils`:
|
||
|
||
```ts title="src/modules/my-notification/service.ts"
|
||
import {
|
||
AbstractNotificationProviderService
|
||
} from "@medusajs/framework/utils"
|
||
|
||
class MyNotificationProviderService extends AbstractNotificationProviderService {
|
||
// TODO add methods
|
||
}
|
||
|
||
export default MyNotificationProviderService
|
||
```
|
||
|
||
### constructor
|
||
|
||
The constructor allows you to access resources from the module's container using the first parameter,
|
||
and the module's options using the second parameter.
|
||
|
||
If you're creating a client or establishing a connection with a third-party service, do it in the constructor.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
import { AbstractNotificationProviderService } from "@medusajs/framework/utils"
|
||
import { Logger } from "@medusajs/framework/types"
|
||
|
||
type InjectedDependencies = {
|
||
logger: Logger
|
||
}
|
||
|
||
type Options = {
|
||
apiKey: string
|
||
}
|
||
|
||
class MyNotificationProviderService extends AbstractNotificationProviderService {
|
||
protected logger_: Logger
|
||
protected options_: Options
|
||
// assuming you're initializing a client
|
||
protected client
|
||
|
||
constructor (
|
||
{ logger }: InjectedDependencies,
|
||
options: Options
|
||
) {
|
||
super()
|
||
|
||
this.logger_ = logger
|
||
this.options_ = options
|
||
|
||
// assuming you're initializing a client
|
||
this.client = new Client(options)
|
||
}
|
||
}
|
||
|
||
export default MyNotificationProviderService
|
||
```
|
||
|
||
### identifier
|
||
|
||
Each notification provider has a unique ID used to identify it.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyNotificationProviderService extends AbstractNotificationProviderService {
|
||
static identifier = "my-notification"
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### validateOptions
|
||
|
||
This method validates the options of the provider set in `medusa-config.ts`.
|
||
Implementing this method is optional. It's useful if your provider requires custom validation.
|
||
|
||
If the options aren't valid, throw an error.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyNotificationProviderService extends AbstractNotificationProviderService {
|
||
static validateOptions(options: Record<any, any>) {
|
||
if (!options.apiKey) {
|
||
throw new MedusaError(
|
||
MedusaError.Types.INVALID_DATA,
|
||
"API key is required in the provider's options."
|
||
)
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"options","type":"`Record<any, any>`","description":"The provider's options.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="validateOptions"/>
|
||
|
||
### send
|
||
|
||
This method is used to send a notification using the third-party provider or your custom logic.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
// other imports...
|
||
import {
|
||
ProviderSendNotificationDTO,
|
||
ProviderSendNotificationResultsDTO
|
||
} from "@medusajs/framework/types"
|
||
|
||
class MyNotificationProviderService extends AbstractNotificationProviderService {
|
||
// ...
|
||
async send(
|
||
notification: ProviderSendNotificationDTO
|
||
): Promise<ProviderSendNotificationResultsDTO> {
|
||
// TODO send the notification using a third-party
|
||
// provider or custom logic.
|
||
// for example:
|
||
return this.client.send({
|
||
email: notification.to,
|
||
template: notification.template,
|
||
template_data: notification.data
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"notification","type":"[ProviderSendNotificationDTO](../../../types/NotificationTypes/interfaces/types.NotificationTypes.ProviderSendNotificationDTO/page.mdx)","description":"The details of the\nnotification to send.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"to","type":"`string`","description":"The recipient of the notification. It can be email, phone number, or username, depending on the channel.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"channel","type":"`string`","description":"The channel through which the notification is sent, such as 'email' or 'sms'","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"template","type":"`string`","description":"The template name in the provider's system.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"from","type":"`null` \\| `string`","description":"The sender of the notification. It can be email, phone number, or username, depending on the channel.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"attachments","type":"`null` \\| [Attachment](../../../types/NotificationTypes/interfaces/types.NotificationTypes.Attachment/page.mdx)[]","description":"Optional attachments for the notification.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"data","type":"`null` \\| `Record<string, unknown>`","description":"The data that gets passed over to the provider for rendering the notification.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"provider_data","type":"`null` \\| `Record<string, unknown>`","description":"Additional data specific to the provider or channel. For example, cc and bcc for emails.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"content","type":"`null` \\| [NotificationContent](../../../types/NotificationTypes/interfaces/types.NotificationTypes.NotificationContent/page.mdx)","description":"The content that gets passed to the provider.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="send"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<[ProviderSendNotificationResultsDTO](../../../types/NotificationTypes/interfaces/types.NotificationTypes.ProviderSendNotificationResultsDTO/page.mdx)>","optional":false,"defaultValue":"","description":"The result of sending\nthe notification.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="send"/>
|
||
|
||
---
|
||
|
||
## 3. Create Module Provider Definition File
|
||
|
||
Create the file `src/modules/my-notification/index.ts` with the following content:
|
||
|
||
```ts title="src/modules/my-notification/index.ts"
|
||
import MyNotificationProviderService from "./service"
|
||
import {
|
||
ModuleProvider,
|
||
Modules
|
||
} from "@medusajs/framework/utils"
|
||
|
||
export default ModuleProvider(Modules.NOTIFICATION, {
|
||
services: [MyNotificationProviderService],
|
||
})
|
||
```
|
||
|
||
This exports the module provider's definition, indicating that the `MyNotificationProviderService` is the module provider's service.
|
||
|
||
<Note title="Tip">
|
||
|
||
A notification module provider can have export multiple provider services, where each are registered as a separate notification provider.
|
||
|
||
</Note>
|
||
|
||
---
|
||
|
||
## 4. Use Module Provider
|
||
|
||
To use your Notification Module Provider, add it to the `providers` array of the Notification Module in `medusa-config.ts`:
|
||
|
||
<Note>
|
||
|
||
The Notification Module accepts one provider per channel.
|
||
|
||
</Note>
|
||
|
||
```ts title="medusa-config.ts"
|
||
module.exports = defineConfig({
|
||
// ...
|
||
modules: [
|
||
{
|
||
resolve: "@medusajs/medusa/notification",
|
||
options: {
|
||
providers: [
|
||
// default provider
|
||
{
|
||
resolve: "@medusajs/medusa/notification-local",
|
||
id: "local",
|
||
options: {
|
||
name: "Local Notification Provider",
|
||
channels: ["feed"],
|
||
},
|
||
},
|
||
{
|
||
// if module provider is in a plugin, use `plugin-name/providers/my-notification`
|
||
resolve: "./src/modules/my-notification",
|
||
id: "my-notification",
|
||
options: {
|
||
channels: ["email"],
|
||
// provider options...
|
||
},
|
||
},
|
||
],
|
||
},
|
||
},
|
||
]
|
||
})
|
||
```
|
||
|
||
Make sure to specify the correct channels for your provider in the `channels` option.
|
||
|
||
---
|
||
|
||
## 5. Test it Out
|
||
|
||
### Create Subscriber
|
||
|
||
To test out the provider, create a subscriber at `src/subscribers/user-created.ts` with the following content:
|
||
|
||
```ts title="src/subscribers/user-created.ts"
|
||
import { Modules } from "@medusajs/framework/utils"
|
||
import {
|
||
SubscriberArgs,
|
||
type SubscriberConfig,
|
||
} from "@medusajs/medusa"
|
||
|
||
export default async function userCreatedHandler({
|
||
event: { data },
|
||
container,
|
||
}: SubscriberArgs<{ id: string }>) {
|
||
const notificationModuleService = container.resolve(
|
||
Modules.NOTIFICATION
|
||
)
|
||
const query = container.resolve("query")
|
||
|
||
const { data: [user] } = await query.graph({
|
||
entity: "user",
|
||
fields: ["*"],
|
||
filters: {
|
||
id: data.id,
|
||
}
|
||
})
|
||
|
||
await notificationModuleService.createNotifications({
|
||
to: user.email,
|
||
channel: "email",
|
||
template: "new-user"
|
||
})
|
||
}
|
||
|
||
export const config: SubscriberConfig = {
|
||
event: "user.created",
|
||
}
|
||
```
|
||
|
||
In the subscriber, you resolve the Notification and User modules. Then, you use the User Module's main service to retrieve the user's details.
|
||
|
||
Finally, you use the Notification Module's main service to send a notification to the user's email through the `email` channel (assuming that's your provider's channel).
|
||
|
||
Make sure to replace the value of `template` to the ID of the template in your provider.
|
||
|
||
### Create User
|
||
|
||
Use the following command to create a user:
|
||
|
||
```bash
|
||
npx medusa user -e admin@test.com -p supersecret
|
||
```
|
||
|
||
After the user is created, the subscriber is executed, sending the notification using your provider.
|