Skip to content

Commit

Permalink
docs: revise events and subscribers
Browse files Browse the repository at this point in the history
  • Loading branch information
shahednasser committed Nov 22, 2024
1 parent b964b45 commit 345bdd1
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 76 deletions.
136 changes: 61 additions & 75 deletions www/apps/book/app/learn/basics/events-and-subscribers/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,77 @@ export const metadata = {

# {metadata.title}

In this chapter, you’ll learn how to handle events with subscribers.
In this chapter, you’ll learn about Medusa's event system, and how to handle events with subscribers.

## What is an Event?
## Handle Core Commerce Flows with Events

When an action is performed in Medusa, such as creating a product, the Medusa application emits an event.
When building commerce digital applications, you'll often need to perform an action after a commerce operation is performed. For example, sending an order confirmation email when the customer places an order.

You can listen to those events and perform an asynchronous action using a subscriber.
In other commerce platforms, it can be tricky to implement this when the commerce operation is performed in the platform's core. You resort to hacky workarounds or extend core services to perform your custom action, which becomes difficult to maintain in the long run and as your application grows.

---
Medusa's event system removes this complexity by emitting events when core commerce features are performed. Your efforts only go into handling these events in subscribers, which are functions executed asynchronously when an event is emitted.

![A diagram showcasing an example of how an event is emitted when an order is placed.](https://res.cloudinary.com/dza7lstvk/image/upload/v1732277948/Medusa%20Book/order-placed-event-example_e4e4kw.jpg)

Subscribers are useful to perform actions that aren't integral to the original flow. For example, you can handle the `order.placed` event in a subscriber that sends a confirmation email to the customer. The subscriber has no impact on the original order-placement flow, as it's executed outside of it.

<Note>

If the action you're performing is integral to the main flow of the core commerce feature, use [workflow hooks](../../advanced-development/workflows/workflow-hooks/page.mdx) instead.

</Note>

### List of Emitted Events

## What is a Subscriber?
Find a list of all emitted events in [this reference](!resources!/events-reference).

A subscriber is a function executed whenever the event it listens to is emitted.
---

### How to Create a Subscriber?
## How to Create a Subscriber?

A subscriber is created in a TypeScript or JavaScript file under the `src/subscribers` directory.
You create a subscriber in a TypeScript or JavaScript file under the `src/subscribers` directory. The file exports the function to execute and the subscriber's configuration that indicate what event(s) it listens to.

For example, create the file `src/subscribers/product-created.ts` with the following content:
For example, create the file `src/subscribers/order-placed.ts` with the following content:

```ts title="src/subscribers/product-created.ts"
import { type SubscriberConfig } from "@medusajs/framework"
import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework"
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
import { sendOrderConfirmationWorkflow } from "../workflows/send-order-confirmation"

// subscriber function
export default async function productCreateHandler() {
console.log("A product was created")
export default async function orderPlacedHandler({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const logger = container.resolve(
ContainerRegistrationKeys.LOGGER
)

logger.info("Sending confirmation email...")

await sendOrderConfirmationWorkflow(container)
.run({
input: {
id: data.id
}
})
}

// subscriber config
export const config: SubscriberConfig = {
event: "product.created",
event: `order.placed`,
}
```

A subscriber file must export:
This subscriber file exports:

- An asynchronous subscriber function that's executed whenever the associated event, which is `order.placed` is triggered.
- A configuration object with an `event` property whose value is the event the subscriber is listening to. You can also pass an array of event names to listen to multiple events in the same subscriber.

- An asynchronous subscriber function that's executed whenever the associated event is triggered.
- A configuration object defining the event this subscriber is listening to.
The subscriber function receives an object as a parameter that has the following properties:

The above subscriber listens to the `product.created` event. Whenever the event is emitted, it logs in the terminal `A product is created`.
- `event`: An object with the event's details. The `data` property contains the data payload of the event emitted, which is the order's ID in this case.
- `container`: The [Medusa container](../medusa-container/page.mdx) that you can use to resolve registered resources.

In the subscriber function, you use the container to resolve the Logger utility and log a message in the console. Also, assuming you have a [workflow](../workflows/page.mdx) that sends an order confirmation email, you execute it in the subscriber.

---

Expand All @@ -55,69 +86,24 @@ To test the subscriber, start the Medusa application:
npm run dev
```

Then, go to the Medusa Admin at `localhost:9000/app` and create a product. Youll see the following messages logged in the Medusa application's terminal:
Then, try placing an order either using Medusa's API routes or the [Next.js Storefront](../../storefront-development/nextjs-starter/page.mdx). You'll see the following message in the terminal:

```bash
info: Processing product.created which has 1 subscribers
A product was created
info: Processing order.placed which has 1 subscribers
Sending confirmation email...
```

The first message indicates that the `product.created` event was emitted, and the second one is the message logged from the subscriber.

---

## When to Use Subscribers

<Note title="Use subscribers when" type="success">

You want to perform an action everytime a specific event is emitted in the Medusa application.

</Note>
The first message indicates that the `order.placed` event was emitted, and the second one is the message logged from the subscriber.

---

## Resolve Resources

The subscriber function accepts an object parameter with the property `container`. Its value is the Medusa container, and you can use it to resolve other resources, such as services.

For example:

export const highlights = [
["7", "container", "Recieve the Medusa Container in the object parameter."],
["10", "resolve", "Resolve the Product Module's main service."],
["10", "Modules.PRODUCT", "The module's registration name imported from `@medusajs/framework/utils`."]
]
## Event Module

```ts title="src/subscribers/product-created.ts" highlights={highlights}
import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework"
import { IProductModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"

export default async function productCreateHandler({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const productModuleService: IProductModuleService =
container.resolve(Modules.PRODUCT)
The subscription and emitting of events is handled by an Event Module, an architectural module that implements the pub/sub functionalities of Medusa's event system.

const productId = data.id
Medusa provides two Event Modules out of the box:

const product = await productModuleService.retrieveProduct(
productId
)
- [Local Event Module](!resources!/architectural-modules/event/local), used by default. It's useful for development, as you don't need additional setup to use it.
- [Redis Event Module](!resources!/architectural-modules/event/redis), which is useful in production. It uses [Redis](https://redis.io/) to implement Medusa's pub/sub events system.

console.log(`The product ${product.title} was created`)
}

export const config: SubscriberConfig = {
event: `product.created`,
}
```

You use the container to resolve the Product Module's main service, then log the title of the created product.

---

## Events List

Find a list of all emitted events in [this reference](!resources!/events-reference).
Medusa's [architecture](../../advanced-development/architecture/overview/page.mdx) also allows you to build a custom Event Module that uses a different service or logic to implement the pub/sub system. Learn how to build an Event Module in [this guide](!resources!/architectural-modules/event/create).
2 changes: 1 addition & 1 deletion www/apps/book/generated/edit-dates.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const generatedEditDates = {
"app/learn/basics/api-routes/page.mdx": "2024-09-11T10:48:31.777Z",
"app/learn/basics/modules-directory-structure/page.mdx": "2024-10-03T13:03:35.957Z",
"app/learn/advanced-development/workflows/access-workflow-errors/page.mdx": "2024-09-18T12:54:04.695Z",
"app/learn/basics/events-and-subscribers/page.mdx": "2024-09-30T08:43:53.131Z",
"app/learn/basics/events-and-subscribers/page.mdx": "2024-11-22T12:19:32.916Z",
"app/learn/advanced-development/modules/container/page.mdx": "2024-11-21T08:59:18.707Z",
"app/learn/advanced-development/workflows/execute-another-workflow/page.mdx": "2024-09-30T08:43:53.129Z",
"app/learn/basics/loaders/page.mdx": "2024-09-03T08:00:45.993Z",
Expand Down

0 comments on commit 345bdd1

Please sign in to comment.