docs: add documentation for v1.8 (#3669)
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
---
|
||||
description: 'In this document, you’ll learn how to create an events module, then test and publish the module as an NPM package.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
# Create Events Module
|
||||
|
||||
In this document, you’ll learn how to create an events module.
|
||||
|
||||
## Overview
|
||||
|
||||
Medusa provides ready-made modules for events, including the local and Redis modules. If you prefer another technology used for managing events, you can build a module locally and use it in your Medusa backend. You can also publish to NPM and reuse it across multiple Medusa backend instances.
|
||||
|
||||
In this document, you’ll learn how to build your own Medusa events module, mainly focusing on creating the event bus service and the available methods you need to implement within your module.
|
||||
|
||||
---
|
||||
|
||||
## (Optional) Step 0: Prepare Module Directory
|
||||
|
||||
Before you start implementing your module, it's recommended to prepare the directory or project holding your custom implementation.
|
||||
|
||||
You can refer to the [Project Preparation step in the Create Module documentation](../modules/create.mdx#optional-step-0-project-preparation) to learn how to do that.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Create the Service
|
||||
|
||||
Create the file `services/event-bus-custom.ts` which will hold your event bus service. Note that the name of the file is recommended to be in the format `event-bus-<service_name>` where `<service_name>` is the name of the service you’re integrating. For example, `event-bus-redis`.
|
||||
|
||||
Add the following content to the file:
|
||||
|
||||
```ts title=services/event-bus-custom.ts
|
||||
import { EmitData, EventBusTypes } from "@medusajs/types"
|
||||
import { AbstractEventBusModuleService } from "@medusajs/utils"
|
||||
|
||||
class CustomEventBus extends AbstractEventBusModuleService {
|
||||
async emit<T>(
|
||||
eventName: string,
|
||||
data: T,
|
||||
options: Record<string, unknown>
|
||||
): Promise<void>;
|
||||
async emit<T>(data: EmitData<T>[]): Promise<void>;
|
||||
async emit(
|
||||
eventName: unknown,
|
||||
data?: unknown,
|
||||
options?: unknown
|
||||
): Promise<void> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
}
|
||||
|
||||
export default CustomEventBus
|
||||
```
|
||||
|
||||
This creates the class `CustomEventBus` that implements the `AbstractEventBusModuleService` class imported from `@medusajs/utils`. Feel free to rename the class to what’s relevant for your event bus service.
|
||||
|
||||
In the class you must implement the `emit` method. You can optionally implement the `subscribe` and `unsubscribe` methods.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Implement Methods
|
||||
|
||||
### Note About the eventToSubscribersMap Property
|
||||
|
||||
The `AbstractEventBusModuleService` implements two methods for handling subscription: `subscribe` and `unsubscribe`. In these methods, the subscribed handler methods are managed within a class property `eventToSubscribersMap`, which is a JavaScript Map. They map keys are the event names, whereas the value of each key is an array of subscribed handler methods.
|
||||
|
||||
In your custom implementation, you can use this property to manage the subscribed handler methods. For example, you can get the subscribers of a method using the `get` method of the map:
|
||||
|
||||
```ts
|
||||
const eventSubscribers =
|
||||
this.eventToSubscribersMap.get(eventName) || []
|
||||
```
|
||||
|
||||
Alternatively, you can implement custom logic for the `subscribe` and `unsubscribe` events, which is explained later in this guide.
|
||||
|
||||
### constructor
|
||||
|
||||
The `constructor` method of a service allows you to prepare any third-party client or service necessary to be used in other methods. It also allows you to get access to the module’s options which are typically defined in `medusa-config.js`, and to other services and resources in the Medusa backend using [dependency injection](../fundamentals/dependency-injection.md).
|
||||
|
||||
Here’s an example of how you can use the `constructor` to store the options of your module:
|
||||
|
||||
<!-- eslint-disable prefer-rest-params -->
|
||||
|
||||
```ts title=services/event-bus-custom.ts
|
||||
class CustomEventBus extends AbstractEventBusModuleService {
|
||||
protected readonly moduleOptions: Record<string, any>
|
||||
|
||||
constructor({
|
||||
// inject resources from the Medusa backend
|
||||
// for example, you can inject the logger
|
||||
logger,
|
||||
}, options) {
|
||||
super(...arguments)
|
||||
this.moduleOptions = options
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### emit
|
||||
|
||||
The `emit` method is used to push an event from Medusa into your messaging system. Typically, the subscribers to that event would then pick up the message and execute their asynchronous tasks.
|
||||
|
||||
The `emit` method has two different signatures:
|
||||
|
||||
1. The first signature accepts three parameters. The first parameter is `eventName` being a required string indicating the name of the event to trigger. The second parameter is `data` being the optional data to send to subscribers of that event. The third optional parameter `options` which can be used to pass options specific to the event bus.
|
||||
2. The second signature accepts one parameter, which is an array of objects having three properties: `eventName`, `data`, and `options`. These are the same as the parameters that can be passed in the first signature. This signature allows emitting more than one event.
|
||||
|
||||
The `options` parameter depends on the event bus integrating. For example, the Redis event bus accept the following options:
|
||||
|
||||
```ts title=services/event-bus-custom.ts
|
||||
type JobData<T> = {
|
||||
eventName: string
|
||||
data: T
|
||||
completedSubscriberIds?: string[] | undefined
|
||||
}
|
||||
```
|
||||
|
||||
You can implement your method in a way that supports both signatures by checking the type of the first input. For example:
|
||||
|
||||
```ts title=services/event-bus-custom.ts
|
||||
class CustomEventBus extends AbstractEventBusModuleService {
|
||||
// ...
|
||||
async emit<T>(
|
||||
eventName: string,
|
||||
data: T,
|
||||
options: Record<string, unknown>
|
||||
): Promise<void>;
|
||||
async emit<T>(data: EmitData<T>[]): Promise<void>;
|
||||
async emit<T>(
|
||||
eventOrData: string | EmitData<T>[],
|
||||
data?: T,
|
||||
options: Record<string, unknown> = {}
|
||||
): Promise<void> {
|
||||
const isBulkEmit = Array.isArray(eventOrData)
|
||||
|
||||
// emit event
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### (optional) subscribe
|
||||
|
||||
As mentioned earlier, this method is already implemented in the `AbstractEventBusModuleService` class. This section explains how you can implement your custom subscribe logic if necessary.
|
||||
|
||||
The `subscribe` method attaches a handler method to the specified event, which is run when the event is triggered. It is typically used inside a subscriber class.
|
||||
|
||||
The `subscribe` method accepts three parameters:
|
||||
|
||||
1. The first parameter `eventName` is a required string. It indicates which event the handler method is subscribing to.
|
||||
2. The second parameter `subscriber` is a required function that performs an action when the event is triggered.
|
||||
3. The third parameter `context` is an optional object that has the property `subscriberId`. Subscriber IDs are useful to differentiate between handler methods when retrying a failed method. It’s also useful for unsubscribing an event handler. Note that if you must implement the mechanism around assigning IDs to subscribers when you override the `subscribe` method.
|
||||
|
||||
The implementation of this method depends on the service you’re using for the event bus:
|
||||
|
||||
```ts title=services/event-bus-custom.ts
|
||||
class CustomEventBus extends AbstractEventBusModuleService {
|
||||
// ...
|
||||
subscribe(
|
||||
eventName: string | symbol,
|
||||
subscriber: EventBusTypes.Subscriber,
|
||||
context?: EventBusTypes.SubscriberContext): this {
|
||||
// TODO implement subscription
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### (optional) unsubscribe
|
||||
|
||||
As mentioned earlier, this method is already implemented in the `AbstractEventBusModuleService` class. This section explains how you can implement your custom unsubscribe logic if necessary.
|
||||
|
||||
The `unsubscribe` method is used to unsubscribe a handler method from an event.
|
||||
|
||||
The `unsubscribe` method accepts three parameters:
|
||||
|
||||
1. The first parameter `eventName` is a required string. It indicates which event the handler method is unsubscribing from.
|
||||
2. The second parameter `subscriber` is a required function that was initially subscribed to the event.
|
||||
3. The third parameter `context` is an optional object that has the property `subscriberId`. It can be used to specify the ID of the subscriber to unsubscribe.
|
||||
|
||||
The implementation of this method depends on the service you’re using for the event bus:
|
||||
|
||||
```ts title=services/event-bus-custom.ts
|
||||
class CustomEventBus extends AbstractEventBusModuleService {
|
||||
// ...
|
||||
unsubscribe(
|
||||
eventName: string | symbol,
|
||||
subscriber: EventBusTypes.Subscriber,
|
||||
context?: EventBusTypes.SubscriberContext): this {
|
||||
// TODO implement subscription
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Export the Service
|
||||
|
||||
After implementing the event bus service, you must export it so that the Medusa backend can use it.
|
||||
|
||||
Create the file `index.ts` with the following content:
|
||||
|
||||
```ts title=services/event-bus-custom.ts
|
||||
import { ModuleExports } from "@medusajs/modules-sdk"
|
||||
|
||||
import { CustomEventBus } from "./services"
|
||||
|
||||
const service = CustomEventBus
|
||||
|
||||
const moduleDefinition: ModuleExports = {
|
||||
service,
|
||||
}
|
||||
|
||||
export default moduleDefinition
|
||||
```
|
||||
|
||||
This exports a module definition, which requires at least a `service`. If you named your service something other than `CustomEventBus`, make sure to replace it with that.
|
||||
|
||||
You can learn more about what other properties you can export in your module definition in the [Create a Module documentation](../modules/create.mdx#step-2-export-module).
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Test your Module
|
||||
|
||||
You can test your module in the Medusa backend by referencing it in the configurations.
|
||||
|
||||
To do that, add the module to the exported configuration in `medusa-config.js` as follows:
|
||||
|
||||
```js title=medusa-config.js
|
||||
module.exports = {
|
||||
// ...
|
||||
modules: {
|
||||
// ...
|
||||
cacheService: {
|
||||
resolve: "path/to/custom-module",
|
||||
options: {
|
||||
// any necessary options
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Make sure to replace the `path/to/custom-module` with a relative path from your Medusa backend to your module. You can learn more about module reference in the [Create Module documentation](../modules/create.mdx#module-reference).
|
||||
|
||||
You can also add any necessary options to the module.
|
||||
|
||||
Then, to test the module, run the Medusa backend which also runs your module:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run start
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## (Optional) Step 5: Publish your Module
|
||||
|
||||
You can publish your events module to NPM. This can be useful if you want to reuse your module across Medusa backend instances, or want to allow other developers to use it.
|
||||
|
||||
You can refer to the [Publish Module documentation](../modules/publish.md) to learn how to publish your module.
|
||||
@@ -7,14 +7,6 @@ addHowToData: true
|
||||
|
||||
In this document, you’ll learn how to create a [Subscriber](./subscribers.mdx) in Medusa that listens to events to perform an action.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Medusa's event system works by pushing data to a Queue that each handler then gets notified of. The queuing system is based on Redis, so it's required for subscribers to work.
|
||||
|
||||
You can learn how to [install Redis](../backend/prepare-environment.mdx#redis) and [configure it with Medusa](../backend/configurations.md#redis) before you get started.
|
||||
|
||||
---
|
||||
|
||||
## Implementation
|
||||
|
||||
A subscriber is a TypeScript or JavaScript file that is created under `src/subscribers`. Its file name, by convension, should be the class name of the subscriber without the word `Subscriber`. For example, if the subscriber is `HelloSubscriber`, the file name should be `hello.ts`.
|
||||
@@ -41,12 +33,25 @@ export default OrderNotifierSubscriber
|
||||
|
||||
This subscriber registers the method `handleOrder` as one of the handlers of the `order.placed` event. The method `handleOrder` will be executed every time an order is placed. It receives the order ID in the `data` parameter. You can then use the order’s details to perform any kind of task you need.
|
||||
|
||||
:::note
|
||||
:::tip
|
||||
|
||||
The `data` object won't contain other order data. Only the ID of the order. You can retrieve the order information using the `orderService`.
|
||||
For the `order.placed` event, the `data` object won't contain other order data. Only the ID of the order. You can retrieve the order information using the `orderService`.
|
||||
|
||||
:::
|
||||
|
||||
### Subscriber ID
|
||||
|
||||
The `subscribe` method of the `eventBusService` accepts a third optional parameter which is a context object. This object has a property `subscriberId` with its value being a string. This ID is useful when there is more than one handler method attached to a single event or if you have multiple Medusa backends running. This allows the events bus service to differentiate between handler methods when retrying a failed one.
|
||||
If a subscriber ID is not passed on subscription, all handler methods are run again. This can lead to data inconsistencies or general unwanted behavior in your system. On the other hand, if you want all handler methods to run again when one of them fails, you can omit passing a subscriber ID.
|
||||
|
||||
An example of using the subscribe method with the third parameter:
|
||||
|
||||
```ts
|
||||
eventBusService.subscribe("order.placed", this.handleOrder, {
|
||||
subscriberId: "my-unique-subscriber",
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using Services in Subscribers
|
||||
@@ -55,7 +60,7 @@ You can access any service through the dependencies injected to your subscriber
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
```ts title=src/subscribers/orderNotifier.ts
|
||||
class OrderNotifierSubscriber {
|
||||
constructor({ productService, eventBusService }) {
|
||||
this.productService = productService
|
||||
@@ -71,7 +76,7 @@ class OrderNotifierSubscriber {
|
||||
|
||||
You can then use `this.productService` anywhere in your subscriber’s methods. For example:
|
||||
|
||||
```ts
|
||||
```ts title=src/subscribers/orderNotifier.ts
|
||||
class OrderNotifierSubscriber {
|
||||
// ...
|
||||
handleOrder = async (data) => {
|
||||
|
||||
@@ -826,6 +826,193 @@ Object of the following format:
|
||||
|
||||
---
|
||||
|
||||
## Inventory Item Events
|
||||
|
||||
This section holds all events related to inventory items.
|
||||
|
||||
<table class="reference-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Event Name
|
||||
</th>
|
||||
<th>
|
||||
Description
|
||||
</th>
|
||||
<th>
|
||||
Event Data Payload
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`inventory-item.created`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when an inventory item is created.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // string ID of the inventory item
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`inventory-item.updated`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when an inventory item is updated.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // string ID of the inventory item
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`inventory-item.deleted`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when an inventory item is deleted.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // string ID of the inventory item
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
---
|
||||
|
||||
## Inventory Level Events
|
||||
|
||||
This section holds all events related to inventory levels.
|
||||
|
||||
<table class="reference-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Event Name
|
||||
</th>
|
||||
<th>
|
||||
Description
|
||||
</th>
|
||||
<th>
|
||||
Event Data Payload
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`inventory-level.created`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when an inventory level is created.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // string ID of the inventory level
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`inventory-level.updated`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when an inventory level is updated.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // string ID of the inventory level
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`inventory-level.deleted`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when an inventory level is deleted, which can be done either directly using its ID or based on the ID of a location. The returned ID depends on how the inventory level was deleted.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // (optional) string ID of the inventory level, available if it was deleted directly
|
||||
location_id // (optional) string ID of location, available if level was deleted by location ID
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
---
|
||||
|
||||
## Invite Events
|
||||
|
||||
This section holds all events related to invites.
|
||||
@@ -1817,7 +2004,7 @@ Triggered when the capturing of a payment fails.
|
||||
|
||||
The entire payment passed as an object. You can refer to the [Payment entity](../../references/entities/classes/Payment.md) for an idea of what fields to expect.
|
||||
|
||||
In addition, an error object is passed within the same object as the Payment provider:
|
||||
In addition, an error object is passed within the same object as the Payment Processor:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
@@ -2076,6 +2263,12 @@ Object of the following format:
|
||||
|
||||
This section holds all events related to product categories.
|
||||
|
||||
:::note
|
||||
|
||||
Product Category feature is currently in beta mode and guarded by a feature flag. You can learn how to enable it in the [Product Categories documentation](../../modules/products/categories.md).
|
||||
|
||||
:::
|
||||
|
||||
<table class="reference-table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -2438,6 +2631,101 @@ Object of the following format:
|
||||
|
||||
---
|
||||
|
||||
## Reservation Item Events
|
||||
|
||||
This section holds all events related to reservation items.
|
||||
|
||||
<table class="reference-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Event Name
|
||||
</th>
|
||||
<th>
|
||||
Description
|
||||
</th>
|
||||
<th>
|
||||
Event Data Payload
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`reservation-item.created`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when a reservation item is created.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // string ID of the reservation item
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`reservation-item.updated`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when an reservation item is updated.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // string ID of the reservation item
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`reservation-item.deleted`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when a reservation item is deleted, which can be done either directly using its ID or based on the ID of a location or a line item. The returned ID depends on how the reservation item was deleted.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // (optional) string ID of the reservation item, available if it was deleted directly
|
||||
location_id // (optional) string ID of location, available if item was deleted by location ID
|
||||
line_item_id // (optional) string ID of line item, available if reservation item was deleted by line item ID
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
---
|
||||
|
||||
## Sales Channel Events
|
||||
|
||||
This section holds all events related to sales channels.
|
||||
@@ -2533,6 +2821,99 @@ Object of the following format:
|
||||
|
||||
---
|
||||
|
||||
## Stock Location Events
|
||||
|
||||
This section holds all events related to stock locations.
|
||||
|
||||
<table class="reference-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Event Name
|
||||
</th>
|
||||
<th>
|
||||
Description
|
||||
</th>
|
||||
<th>
|
||||
Event Data Payload
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`stock-location.created`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when a stock location is created.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // string ID of the stock location
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`stock-location.updated`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when an stock location is updated.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // string ID of the stock location
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`stock-location.deleted`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggered when a stock location is deleted.
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
id // string ID of the stock location
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
---
|
||||
|
||||
## Swap Events
|
||||
|
||||
This section holds all events related to swaps.
|
||||
@@ -2958,6 +3339,6 @@ Object of the following format:
|
||||
|
||||
## See Also
|
||||
|
||||
- [Events architecture overview](./index.md)
|
||||
- [Events overview](./index.mdx)
|
||||
- [Use services in subscribers](./create-subscriber.md#using-services-in-subscribers)
|
||||
- [Create a notification provider](../notification/overview.mdx)
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
---
|
||||
description: 'Learn how the events system is implemented in Medusa. It is built on a publish-subscribe architecture. The Medusa core publishes events when certain actions take place.'
|
||||
---
|
||||
|
||||
# Events Architecture
|
||||
|
||||
In this document, you'll learn how the events system is implemented in Medusa.
|
||||
|
||||
## Overview
|
||||
|
||||
The events system in Medusa is built on a publish/subscribe architecture. The Medusa core publishes events when certain actions take place.
|
||||
|
||||
Those events can be subscribed to using subscribers. When you subscribe to an event, you can perform a task asynchronusly every time the event is triggered.
|
||||
|
||||
:::info
|
||||
|
||||
You can learn more about subscribers and their use cases in the [Subscribers](./subscribers.mdx) documentation.
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Publishing and Subscribing
|
||||
|
||||
The `EventBusService` is responsible for publishing and processing events.
|
||||
|
||||
:::note
|
||||
|
||||
The current implementation of the `EventBusService` is powered by Redis. However, an upcoming version of Medusa introduces an event bus module. This will allow you to use any publishing and subscribing provider. That will not change the general purpose and flow of the `EventBusService`.
|
||||
|
||||
:::
|
||||
|
||||
The `EventBusService` exposes two methods in its public API for event processing; `emit` and `subscribe`.
|
||||
|
||||
### emit
|
||||
|
||||
The `emit` method accepts as a first parameter the event name. It adds it to a Bull queue (powered by Redis) as a job, and processes it asynchronously.
|
||||
|
||||
The second parameter contains any data that should be emitted with the event. Subscribers that handle the event will receive that data as a method parameter.
|
||||
|
||||
The third parameter is an options object. It accepts options related to the number of retries if a subscriber handling the event fails, the delay time, and more. The options are explained in a [later section](#retrying-handlers)
|
||||
|
||||
The `emit` method has the following signature:
|
||||
|
||||
```ts
|
||||
export default class EventBusService {
|
||||
// ...
|
||||
async emit<T>(
|
||||
eventName: string,
|
||||
data: T,
|
||||
options: Record<string, unknown> &
|
||||
EmitOptions = { attempts: 1 }
|
||||
): Promise<StagedJob | void>
|
||||
}
|
||||
```
|
||||
|
||||
Here's an example of how you can emit an event using the `EventBusService`:
|
||||
|
||||
```ts
|
||||
eventBusService.emit(
|
||||
"product.created",
|
||||
{ id: "prod_..." },
|
||||
{ attempts: 2 }
|
||||
)
|
||||
```
|
||||
|
||||
The `EventBusService` emits the event `product.created` by passing the event name as a first argument. An object is passed as a second argument which is the data passed to the event handler methods in subscribers. This object contains the ID of the product.
|
||||
|
||||
Options are passed in the third argument. The `attempt` property specifies how many times the subscriber should be retried if it fails (by default it's one).
|
||||
|
||||
### subscribe
|
||||
|
||||
The `subscribe` method will attach a handler method to the specified event, which is run when the event is triggered. It is usually used insde a subscriber class.
|
||||
|
||||
The `subscribe` method accepts the event name as the first parameter. This is the event that the handler method will attach to.
|
||||
|
||||
The second parameter is the handler method that will be triggered when the event is emitted.
|
||||
|
||||
The third parameter is an optional `context` parameter. It allows you to configure the ID of the handler method.
|
||||
|
||||
The `subscribe` method has the following signature:
|
||||
|
||||
```ts
|
||||
export default class EventBusService {
|
||||
// ...
|
||||
subscribe(
|
||||
event: string | symbol,
|
||||
subscriber: Subscriber,
|
||||
context?: SubscriberContext
|
||||
): this
|
||||
}
|
||||
```
|
||||
|
||||
Here's an example of how you can subscribe to an event using the `EventBusService`:
|
||||
|
||||
```ts title=src/subscribers/my.ts
|
||||
import { EventBusService } from "@medusajs/medusa"
|
||||
|
||||
class MySubscriber {
|
||||
constructor({
|
||||
eventBusService: EventBusService,
|
||||
}) {
|
||||
eventBusService.subscribe("product.created", (data) => {
|
||||
// TODO handle event
|
||||
console.log(data.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the constructor of a subscriber, you use the `EventBusService` to subscribe to the event `product.created`. In the handler method, you can perform a task every time the product is created. Notice how the handler method accepts the `data` as a parameter as explain in the previous section.
|
||||
|
||||
:::note
|
||||
|
||||
You can learn more about how to create a subscriber in [this documentation](./create-subscriber.md)
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Processing Events
|
||||
|
||||
In the `EventBusService` service, the `worker_` method defines the logic run for each event emitted into the queue.
|
||||
|
||||
By default, all handler methods to that event are retrieved and, for each of the them, the stored data provided as a second parameter in `emit` is passed as an argument.
|
||||
|
||||
---
|
||||
|
||||
## Retrying Handlers
|
||||
|
||||
A handler method might fail to process an event. This could happen because it communicates with a third party service currently down or due to an error in its logic.
|
||||
|
||||
In some cases, you might want to retry those failed handlers.
|
||||
|
||||
As briefly explained earlier, you can pass options when emitting an event as a third argument that are used to configure how the queue worker processes your job. If you pass `attempts` upon emitting the event, the processing of a handler method is retried when it fails.
|
||||
|
||||
Aside from `attempts`, there are other options to futher configure the retry mechanism:
|
||||
|
||||
```ts
|
||||
type EmitOptions = {
|
||||
delay?: number
|
||||
attempts: number
|
||||
backoff?: {
|
||||
type: "fixed" | "exponential"
|
||||
delay: number
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's what each of these options mean:
|
||||
|
||||
- `delay`: delay the triggering of the handler methods by a number of milliseconds.
|
||||
- `attempts`: the number of times a subscriber handler should be retried when it fails.
|
||||
- `backoff`: the wait time between each retry
|
||||
|
||||
### Note on Subscriber IDs
|
||||
|
||||
If you have more than one handler methods attached to a single event, or if you have multiple backend instances running, you must pass a subscriber ID as a third parameter to the `subscribe` method. This allows the `EventBusService` to differentiate between handler methods when retrying a failed one.
|
||||
|
||||
If a subscriber ID is not passed on subscription, all handler methods are run again. This can lead to data inconsistencies or general unwanted behavior in your system.
|
||||
|
||||
On the other hand, if you want all handler methods to run again when one of them fails, you can omit passing a subscriber ID.
|
||||
|
||||
An example of passing a subscriber ID:
|
||||
|
||||
```ts title=src/subscribers/my.ts
|
||||
import { EventBusService } from "@medusajs/medusa"
|
||||
|
||||
class MySubscriber {
|
||||
constructor({
|
||||
eventBusService: EventBusService,
|
||||
}) {
|
||||
eventBusService.subscribe(
|
||||
"product.created",
|
||||
(data) => {
|
||||
// TODO handle event
|
||||
console.log(data.id)
|
||||
},
|
||||
"my-unique-subscriber")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
You can learn more about subscriber IDs in [Bull's documentation](https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#queueadd).
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Database transactions
|
||||
|
||||
<!-- vale docs.Acronyms = NO -->
|
||||
|
||||
Transactions in Medusa ensure atomicity, consistency, isolation, and durability, or ACID, guarantees for operations in the Medusa core.
|
||||
|
||||
<!-- vale docs.Acronyms = YES -->
|
||||
|
||||
In many cases, [services](../services/overview.mdx) typically update resources in the database and emit an event within a transactional operation. To ensure that these events don't cause data inconsistencies (for example, a plugin subscribes to an event to contact a third-party service, but the transaction fails) the concept of a staged job is introduced.
|
||||
|
||||
Instead of events being processed immediately, they're stored in the database as a staged job until they're ready. In other words, until the transaction has succeeded.
|
||||
|
||||
This rather complex logic is abstracted away from the consumers of the `EventBusService`, but here's an example of the flow when an API request is made:
|
||||
|
||||
1. API request starts.
|
||||
2. Transaction is initiated.
|
||||
3. Service layer performs some logic.
|
||||
4. Events are emitted and stored in the database for eventual processing.
|
||||
5. Transaction is committed.
|
||||
6. API request ends.
|
||||
7. Events in the database become visible.
|
||||
|
||||
To pull staged jobs from the database, a separate enqueuer polls the database every three seconds to discover new visible jobs. These jobs are then added to the queue and processed as described in the [Processing](#processing-events) section earlier.
|
||||
|
||||
:::info
|
||||
|
||||
This pattern is heavily inspired by the [Transactionally-staged Job Drain described in this blog post](https://brandur.org/job-drain).
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Events reference](./events-list.md)
|
||||
- [Create a subscriber](./create-subscriber.md)
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
description: 'Learn how the events system is implemented in Medusa. It is built on a publish-subscribe architecture. The Medusa core publishes events when certain actions take place.'
|
||||
---
|
||||
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import Icons from '@theme/Icon';
|
||||
|
||||
# Events
|
||||
|
||||
In this document, you’ll learn what events are and why they’re useful in Medusa.
|
||||
|
||||
## Overview
|
||||
|
||||
Events are used in Medusa to inform different parts of the commerce ecosystem that this event occurred. For example, when an order is placed, the `order.placed` event is triggered, which informs notification services like SendGrid to send a confirmation email to the customer.
|
||||
|
||||
The events system in Medusa is built on a publish/subscribe architecture. The Medusa core publish an event when an action takes place, and modules, plugins, or other forms of customizations can subscribe to that event. [Subscribers](./subscribers.mdx) can then perform a task asynchronously when the event is triggered.
|
||||
|
||||
Although the core implements the main logic behind the events system, you’ll need to use an event module that takes care of the publish/subscribe functionality such as subscribing and emitting events. Medusa provides modules that you can use both for development and production, including Redis and Local modules.
|
||||
|
||||
---
|
||||
|
||||
## Database Transactions
|
||||
|
||||
Transactions in Medusa ensure Atomicity, Consistency, Isolation, and Durability (ACID) guarantees for operations in the Medusa core.
|
||||
|
||||
In many cases, services typically update resources in the database and emit an event within a transactional operation. To ensure that these events don't cause data inconsistencies (for example, a plugin subscribes to an event to contact a third-party service, but the transaction fails) the concept of a staged job is introduced.
|
||||
|
||||
Instead of events being processed immediately, they're stored in the database as a staged job until they're ready. In other words, until the transaction has succeeded.
|
||||
|
||||
This rather complex logic is abstracted away from the consumers of the EventBusService, but here's an example of the flow when an API request is made:
|
||||
|
||||
- API request starts.
|
||||
- Transaction is initiated.
|
||||
- Service layer performs some logic.
|
||||
- Events are emitted and stored in the database for eventual processing.
|
||||
- Transaction is committed.
|
||||
- API request ends.
|
||||
- Events in the database become visible.
|
||||
|
||||
To pull staged jobs from the database, a separate enqueuer polls the database every three seconds to discover new visible jobs. These jobs are then added to the queue and processed as described in the Processing section earlier.
|
||||
|
||||
:::note
|
||||
|
||||
This pattern is heavily inspired by the Transactionally-staged Job Drain described in this blog post.
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Emitting Events
|
||||
|
||||
You can emit events in Medusa using the `EventBusService`. For example:
|
||||
|
||||
```ts
|
||||
this.eventBusService.emit("custom-event", {
|
||||
// attach any data to the event
|
||||
})
|
||||
```
|
||||
|
||||
You can also emit more than one event:
|
||||
|
||||
```ts
|
||||
this.eventBusService.emit([
|
||||
{
|
||||
eventName: "custom-event-1",
|
||||
data: {
|
||||
// attach any data to the event
|
||||
},
|
||||
},
|
||||
{
|
||||
eventName: "custom-event-2",
|
||||
data: {
|
||||
// attach any data to the event
|
||||
},
|
||||
},
|
||||
])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Available Modules
|
||||
|
||||
Medusa’s default starter project comes with the local event module (`@medusajs/event-bus-local`). For production environments, it’s recommended to use the Redis event module package (`@medusajs/event-bus-redis`) that you can install.
|
||||
|
||||
<DocCardList colSize={6} items={[
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/events/modules/redis',
|
||||
label: 'Redis',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to install Redis events module in Medusa.'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/events/modules/local',
|
||||
label: 'Local',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to install local events module in Medusa.'
|
||||
}
|
||||
},
|
||||
]} />
|
||||
|
||||
---
|
||||
|
||||
## Custom Development
|
||||
|
||||
Developers can create custom event modules, allowing them to integrate any third-party services or logic to handle this functionality. Developers can also create and use subscribers to handle events in Medusa.
|
||||
|
||||
<DocCardList colSize={6} items={[
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/events/create-module',
|
||||
label: 'Create an Event Module',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to create an event module.'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/events/create-subscriber',
|
||||
label: 'Create a Subscriber',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to create a subscriber.'
|
||||
}
|
||||
},
|
||||
]} />
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
description: 'In this document, you’ll learn about the local events module and how you can install it in your Medusa backend.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
# Local Events Module
|
||||
|
||||
In this document, you’ll learn about the local events module and how you can install it in your Medusa backend.
|
||||
|
||||
## Overview
|
||||
|
||||
Medusa’s modular architecture allows developers to extend or completely replace the logic used for events. You can create a custom module, or you can use the modules Medusa provides.
|
||||
|
||||
One of these modules is the local events module. This module allows you to utilize Node EventEmitter for the events system in Medusa. The Node EventEmitter is limited to a single process environment. This module is useful for development and testing, but it’s recommended to use the [Redis events module](./redis.md) in production.
|
||||
|
||||
This document will you guide you through installing the local events module.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
It’s assumed you already have a Medusa backend installed. If not, you can learn how to install it by following [this guide](../../backend/install.mdx).
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Install the Module
|
||||
|
||||
In the root directory of your Medusa backend, install the Redis events module with the following command:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @medusajs/event-bus-local
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Add Configuration
|
||||
|
||||
In `medusa-config.js`, add the following to the exported object:
|
||||
|
||||
```js title=medusa-config.js
|
||||
module.exports = {
|
||||
// ...
|
||||
modules: {
|
||||
// ...
|
||||
eventBus: {
|
||||
resolve: "@medusajs/event-bus-local",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This registers the local events module as the main events service to use. This module does not require any options.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Test Module
|
||||
|
||||
To test the module, run the following command to start the Medusa backend:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run start
|
||||
```
|
||||
|
||||
If the module was installed successfully, you should see the following message in the logs:
|
||||
|
||||
```bash noCopy noReport
|
||||
Local Event Bus installed. This is not recommended for production.
|
||||
```
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
description: 'In this document, you’ll learn about the Redis events module and how you can install it in your Medusa backend.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
# Redis Events Module
|
||||
|
||||
In this document, you’ll learn about the Redis events module and how you can install it in your Medusa backend.
|
||||
|
||||
## Overview
|
||||
|
||||
Medusa’s modular architecture allows developers to extend or completely replace the logic used for events. You can create a custom module, or you can use the modules Medusa provides.
|
||||
|
||||
One of these modules is the Redis module. This module allows you to utilize Redis for the event bus functionality. When installed, the Medusa’s events system is powered by BullMQ and `io-redis`. BullMQ is responsible for the message queue and worker, and `io-redis` is the underlying Redis client that BullMQ connects to for events storage.
|
||||
|
||||
This document will you guide you through installing the Redis module.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Medusa Backend
|
||||
|
||||
It’s assumed you already have a Medusa backend installed. If not, you can learn how to install it by following [this guide](../../backend/install.mdx).
|
||||
|
||||
### Redis
|
||||
|
||||
You must have Redis installed and configured in your Medusa backend. You can learn how to install Redis in [their documentation](https://redis.io/docs/getting-started/installation/).
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Install the Module
|
||||
|
||||
In the root directory of your Medusa backend, install the Redis events module with the following command:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @medusajs/event-bus-redis
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Add Environment Variable
|
||||
|
||||
The Redis events module requires a connection URL to Redis as part of its options. If you don’t already have an environment variable set for a Redis URL, make sure to add one:
|
||||
|
||||
```bash
|
||||
EVENTS_REDIS_URL=<YOUR_REDIS_URL>
|
||||
```
|
||||
|
||||
Where `<YOUR_REDIS_URL>` is a connection URL to your Redis instance.
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Add Configuration
|
||||
|
||||
In `medusa-config.js`, add the following to the exported object:
|
||||
|
||||
```js title=medusa-config.js
|
||||
module.exports = {
|
||||
// ...
|
||||
modules: {
|
||||
// ...
|
||||
eventBus: {
|
||||
resolve: "@medusajs/event-bus-redis",
|
||||
options: {
|
||||
redisUrl: process.env.EVENTS_REDIS_URL,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This registers the Redis events module as the main event bus service to use. In the options, you pass `redisUrl` with the value being the environment variable you set. This is the only required option.
|
||||
|
||||
Other available options include:
|
||||
|
||||
- `queueName`: a string indicating the name of the BullMQ queue. By default, it’s `events-queue`.
|
||||
- `queueOptions`: an object containing options for the BullMQ queue. You can learn about available options in [BullMQ’s documentation](https://api.docs.bullmq.io/interfaces/QueueOptions.html). By default, it’s an empty object.
|
||||
- `redisOptions`: an object containing options for the Redis instance. You can learn about available options in [io-redis’s documentation](https://luin.github.io/ioredis/index.html#RedisOptions). By default, it’s an empty object.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Test Module
|
||||
|
||||
To test the module, run the following command to start the Medusa backend:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run start
|
||||
```
|
||||
|
||||
If the module was installed successfully, you should see the following message in the logs:
|
||||
|
||||
```bash noCopy noReport
|
||||
Connection to Redis in module 'event-bus-redis' established
|
||||
```
|
||||
@@ -9,16 +9,6 @@ import Icons from '@theme/Icon';
|
||||
|
||||
In this document, you'll learn what Subscribers are in Medusa.
|
||||
|
||||
## What are Events
|
||||
|
||||
In Medusa, there are events that are emitted when a certain action occurs. For example, if a customer places an order, the `order.placed` event is emitted with the order data.
|
||||
|
||||
The purpose of these events is to allow other parts of the platform, or third-party integrations, to listen to those events and perform a certain action. That is done by creating subscribers.
|
||||
|
||||
Medusa's queuing and events system is handled by Redis. So, you must have [Redis configured](../backend/prepare-environment.mdx#redis) on your backend to use subscribers.
|
||||
|
||||
---
|
||||
|
||||
## What are Subscribers
|
||||
|
||||
Subscribers register handlers for an events and allows you to perform an action when that event occurs. For example, if you want to send your customer an email when they place an order, then you can listen to the `order.placed` event and send the email when the event is emitted.
|
||||
|
||||
Reference in New Issue
Block a user