docs: revise main docs outline (#10502)

This commit is contained in:
Shahed Nasser
2024-12-09 13:54:42 +02:00
committed by GitHub
parent c8cb9b5c1a
commit 0ae98c51eb
141 changed files with 814 additions and 1181 deletions
@@ -0,0 +1,69 @@
import { TypeList } from "docs-ui"
export const metadata = {
title: `${pageNumber} Event Data Payload`,
}
# {metadata.title}
In this chapter, you'll learn how subscribers receive an event's data payload.
## Access Event's Data Payload
When events are emitted, theyre emitted with a data payload.
The object that the subscriber function receives as a parameter has an `event` property, which is an object holding the event payload in a `data` property with additional context.
For example:
export const highlights = [
["7", "event", "The event details."],
["8", "{ id: string }", "The type of expected data payloads."],
]
```ts title="src/subscribers/product-created.ts" highlights={highlights} collapsibleLines="1-5" expandButtonLabel="Show Imports"
import type {
SubscriberArgs,
SubscriberConfig,
} from "@medusajs/framework"
export default async function productCreateHandler({
event,
}: SubscriberArgs<{ id: string }>) {
const productId = event.data.id
console.log(`The product ${productId} was created`)
}
export const config: SubscriberConfig = {
event: "product.created",
}
```
The `event` object has the following properties:
<TypeList types={[
{
name: "data",
type: "`object`",
description: "The data payload of the event. Its properties are different for each event."
},
{
name: "name",
type: "string",
description: "The name of the triggered event."
},
{
name: "metadata",
type: "`object`",
description: "Additional data and context of the emitted event.",
optional: true
},
]} sectionTitle="Access Event's Data Payload" />
This logs the product ID received in the `product.created` events data payload to the console.
{/* ---
## List of Events with Data Payload
Refer to [this reference](!resources!/events-reference) for a full list of events emitted by Medusa and their data payloads. */}
@@ -0,0 +1,193 @@
import { CodeTabs, CodeTab } from "docs-ui"
export const metadata = {
title: `${pageNumber} Emit Workflow and Service Events`,
}
# {metadata.title}
In this chapter, you'll learn about event types and how to emit an event in a service or workflow.
## Event Types
In your customization, you can emit an event, then listen to it in a subscriber and perform an asynchronus action, such as send a notification or data to a third-party system.
There are two types of events in Medusa:
1. Worflow event: an event that's emitted in a workflow after a commerce feature is performed. For example, Medusa emits the `order.placed` event after a cart is completed.
2. Service event: an event that's emitted to track, trace, or debug processes under the hood. For example, you can emit an event with an audit trail.
### Which Event Type to Use?
**Workflow events** are the most common event type in development, as most custom features and customizations are built around workflows.
Some examples of workflow events:
1. When a user creates a blog post and you're emitting an event to send a newsletter email.
2. When you finish syncing products to a third-party system and you want to notify the admin user of new products added.
3. When a customer purchases a digital product and you want to generate and send it to them.
You should only go for a **service event** if you're emitting an event for processes under the hood that don't directly affect front-facing features.
Some examples of service events:
1. When you're tracing data manipulation and changes, and you want to track every time some custom data is changed.
2. When you're syncing data with a search engine.
---
## Emit Event in a Workflow
To emit a workflow event, use the `emitEventStep` helper step provided in the `@medusajs/medusa/core-flows` package.
For example:
export const highlights = [
["13", "emitEventStep", "Emit an event."],
["14", `eventName`, "The name of the event to emit."],
["15", "data", "The data payload to pass with the event."]
]
```ts highlights={highlights}
import {
createWorkflow,
} from "@medusajs/framework/workflows-sdk"
import {
emitEventStep,
} from "@medusajs/medusa/core-flows"
const helloWorldWorkflow = createWorkflow(
"hello-world",
() => {
// ...
emitEventStep({
eventName: "custom.created",
data: {
id: "123",
// other data payload
},
})
}
)
```
The `emitEventStep` accepts an object having the following properties:
- `eventName`: The event's name.
- `data`: The data payload as an object. You can pass any properties in the object, and subscribers listening to the event will receive this data in the event's payload.
In this example, you emit the event `custom.created` and pass in the data payload an ID property.
### Test it Out
If you execute the workflow, the event is emitted and you can see it in your application's logs.
Any subscribers listening to the event are executed.
---
## Emit Event in a Service
To emit a service event:
1. Resolve `event_bus` from the module's container in your service's constructor:
<CodeTabs group="service_type">
<CodeTab label="Extending Service Factory" value="service_factory">
```ts title="src/modules/hello/service.ts" highlights={["9"]}
import { IEventBusService } from "@medusajs/framework/types"
import { MedusaService } from "@medusajs/framework/utils"
class HelloModuleService extends MedusaService({
MyCustom,
}){
protected eventBusService_: AbstractEventBusModuleService
constructor({ event_bus }) {
super(...arguments)
this.eventBusService_ = event_bus
}
}
```
</CodeTab>
<CodeTab label="Without Service Factory" value="no_service_factory">
```ts title="src/modules/hello/service.ts" highlights={["6"]}
import { IEventBusService } from "@medusajs/framework/types"
class HelloModuleService {
protected eventBusService_: AbstractEventBusModuleService
constructor({ event_bus }) {
this.eventBusService_ = event_bus
}
}
```
</CodeTab>
</CodeTabs>
2. Use the event bus service's `emit` method in the service's methods to emit an event:
export const serviceHighlights = [
["6", "emit", "Emit an event."],
["7", "name", "The name of the event to emit."],
["8", "data", "The data payload to pass with the event."]
]
```ts title="src/modules/hello/service.ts" highlights={serviceHighlights}
class HelloModuleService {
// ...
performAction() {
// TODO perform action
this.eventBusService_.emit({
name: "custom.event",
data: {
id: "123",
// other data payload
},
})
}
}
```
The method accepts an object having the following properties:
- `name`: The event's name.
- `data`: The data payload as an object. You can pass any properties in the object, and subscribers listening to the event will receive this data in the event's payload.
3. By default, the Event Module's service isn't injected into your module's container. To add it to the container, pass it in the module's registration object in `medusa-config.ts` in the `dependencies` property:
export const depsHighlight = [
["8", "dependencies", "An array of module registration names to inject into the Module's container."],
]
```ts title="medusa-config.ts" highlights={depsHighlight}
import { Modules } from "@medusajs/framework/utils"
module.exports = defineConfig({
// ...
modules: [
{
resolve: "./src/modules/hello",
dependencies: [
Modules.EVENT_BUS,
],
},
],
})
```
The `dependencies` property accepts an array of module registration keys. The specified modules' main services are injected into the module's container.
That's how you can resolve it in your module's main service's constructor.
### Test it Out
If you execute the `performAction` method of your service, the event is emitted and you can see it in your application's logs.
Any subscribers listening to the event are also executed.
@@ -0,0 +1,108 @@
export const metadata = {
title: `${pageNumber} Events and Subscribers`,
}
# {metadata.title}
In this chapter, youll learn about Medusa's event system, and how to handle events with subscribers.
## Handle Core Commerce Flows with Events
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, or syncing data that's updated in Medusa to a third-party system.
Medusa emits events when core commerce features are performed, and you can listen to and handle these events in asynchronous functions. You can think of Medusa's events like you'd think about webhooks in other commerce platforms, but instead of having to setup separate applications to handle webhooks, your efforts only go into writing the logic right in your Medusa codebase.
You listen to an event in a subscriber, which is an asynchronous function that's executed when its associated 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](../workflows/workflow-hooks/page.mdx) instead.
</Note>
### List of Emitted Events
Find a list of all emitted events in [this reference](!resources!/events-reference).
---
## How to Create a Subscriber?
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/order-placed.ts` with the following content:
![Example of subscriber file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732866244/Medusa%20Book/subscriber-dir-overview_pusyeu.jpg)
```ts title="src/subscribers/product-created.ts"
import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework"
import { sendOrderConfirmationWorkflow } from "../workflows/send-order-confirmation"
export default async function orderPlacedHandler({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const logger = container.resolve("logger")
logger.info("Sending confirmation email...")
await sendOrderConfirmationWorkflow(container)
.run({
input: {
id: data.id,
},
})
}
export const config: SubscriberConfig = {
event: `order.placed`,
}
```
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.
The subscriber function receives an object as a parameter that has the following properties:
- `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.
---
## Test the Subscriber
To test the subscriber, start the Medusa application:
```bash npm2yarn
npm run dev
```
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 order.placed which has 1 subscribers
Sending confirmation email...
```
The first message indicates that the `order.placed` event was emitted, and the second one is the message logged from the subscriber.
---
## Event Module
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.
Medusa provides two Event Modules out of the box:
- [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.
Medusa's [architecture](../../introduction/architecture/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).