docs: publish restructure (#3496)
* docs: added features and guides overview page * added image * added version 2 * added version 3 * added version 4 * docs: implemented new color scheme * docs: redesigned sidebar (#3193) * docs: redesigned navbar for restructure (#3199) * docs: redesigned footer (#3209) * docs: redesigned cards (#3230) * docs: redesigned admonitions (#3231) * docs: redesign announcement bar (#3236) * docs: redesigned large cards (#3239) * docs: redesigned code blocks (#3253) * docs: redesigned search modal and page (#3264) * docs: redesigned doc footer (#3268) * docs: added new sidebars + refactored css and assets (#3279) * docs: redesigned api reference sidebar * docs: refactored css * docs: added code tabs transition * docs: added new sidebars * removed unused assets * remove unusued assets * Fix deploy errors * fix incorrect link * docs: fixed code responsivity + missing icons (#3283) * docs: changed icons (#3296) * docs: design fixes to the sidebar (#3297) * redesign fixes * docs: small design fixes * docs: several design fixes after restructure (#3299) * docs: bordered icon fixes * docs: desgin fixes * fixes to code blocks and sidebar scroll * design adjustments * docs: restructured homepage (#3305) * docs: restructured homepage * design fixes * fixed core concepts icon * docs: added core concepts page (#3318) * docs: restructured homepage * design fixes * docs: added core concepts page * changed text of different components * docs: added architecture link * added missing prop for user guide * docs: added regions overview page (#3327) * docs: added regions overview * moved region pages to new structure * docs: fixed description of regions architecture page * small changes * small fix * docs: added customers overview page (#3331) * docs: added regions overview * moved region pages to new structure * docs: fixed description of regions architecture page * small changes * small fix * docs: added customers overview page * fix link * resolve link issues * docs: updated regions architecture image * docs: second-iteration fixes (#3347) * docs: redesigned document * design fixes * docs: added products overview page (#3354) * docs: added carts overview page (#3363) * docs: added orders overview (#3364) * docs: added orders overview * added links in overview * docs: added vercel redirects * docs: added soon badge for cards (#3389) * docs: resolved feedback changes + organized troubleshooting pages (#3409) * docs: resolved feedback changes * added extra line * docs: changed icons for restructure (#3421) * docs: added taxes overview page (#3422) * docs: added taxes overview page * docs: fix sidebar label * added link to taxes overview page * fixed link * docs: fixed sidebar scroll (#3429) * docs: added discounts overview (#3432) * docs: added discounts overview * fixed links * docs: added gift cards overview (#3433) * docs: added price lists overview page (#3440) * docs: added price lists overview page * fixed links * docs: added sales channels overview page (#3441) * docs: added sales overview page * fixed links * docs: added users overview (#3443) * docs: fixed sidebar border height (#3444) * docs: fixed sidebar border height * fixed svg markup * docs: added possible solutions to feedback component (#3449) * docs: added several overview pages + restructured files (#3463) * docs: added several overview pages * fixed links * docs: added feature flags + PAK overview pages (#3464) * docs: added feature flags + PAK overview pages * fixed links * fix link * fix link * fixed links colors * docs: added strategies overview page (#3468) * docs: automated upgrade guide (#3470) * docs: automated upgrade guide * fixed vercel redirect * docs: restructured files in docs codebase (#3475) * docs: restructured files * docs: fixed eslint exception * docs: finished restructure loose-ends (#3493) * fixed uses of backend * docs: finished loose ends * eslint fixes * fixed links * merged master * added update instructions for v1.7.12
This commit is contained in:
94
docs/content/development/events/create-subscriber.md
Normal file
94
docs/content/development/events/create-subscriber.md
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
description: 'Learn how to create a subscriber in Medusa. You can use subscribers to implement functionalities like sending an order confirmation email.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
# How to Create a Subscriber
|
||||
|
||||
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`.
|
||||
|
||||
After creating the file under `src/subscribers`, in the constructor of your subscriber, listen to events using `eventBusService.subscribe` , where `eventBusService` is a service injected into your subscriber’s constructor.
|
||||
|
||||
The `eventBusService.subscribe` method receives the name of the event as a first parameter and as a second parameter a method in your subscriber that will handle this event.
|
||||
|
||||
For example, here is the `OrderNotifierSubscriber` class created in `src/subscribers/orderNotifier.ts`:
|
||||
|
||||
```ts title=src/subscribers/orderNotifier.ts
|
||||
class OrderNotifierSubscriber {
|
||||
constructor({ eventBusService }) {
|
||||
eventBusService.subscribe("order.placed", this.handleOrder)
|
||||
}
|
||||
|
||||
handleOrder = async (data) => {
|
||||
console.log("New Order: " + data.id)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
The `data` object won't contain other order data. Only the ID of the order. You can retrieve the order information using the `orderService`.
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Using Services in Subscribers
|
||||
|
||||
You can access any service through the dependencies injected to your subscriber’s constructor.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
class OrderNotifierSubscriber {
|
||||
constructor({ productService, eventBusService }) {
|
||||
this.productService = productService
|
||||
|
||||
eventBusService.subscribe(
|
||||
"order.placed",
|
||||
this.handleOrder
|
||||
)
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can then use `this.productService` anywhere in your subscriber’s methods. For example:
|
||||
|
||||
```ts
|
||||
class OrderNotifierSubscriber {
|
||||
// ...
|
||||
handleOrder = async (data) => {
|
||||
// ...
|
||||
const product = this.productService.list()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
|
||||
When using attributes defined in the subscriber, such as the `productService` in the example above, you must use an arrow function to declare the method. Otherwise, the attribute will be undefined when used.
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Create a Plugin](../plugins/create.md)
|
||||
2974
docs/content/development/events/events-list.md
Normal file
2974
docs/content/development/events/events-list.md
Normal file
File diff suppressed because it is too large
Load Diff
227
docs/content/development/events/index.md
Normal file
227
docs/content/development/events/index.md
Normal file
@@ -0,0 +1,227 @@
|
||||
---
|
||||
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)
|
||||
55
docs/content/development/events/subscribers.mdx
Normal file
55
docs/content/development/events/subscribers.mdx
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
description: 'Learn what subscribers are in Medusa. Subscribers are used to listen to triggered events to perform an action.'
|
||||
---
|
||||
|
||||
import DocCard from '@theme/DocCard';
|
||||
import Icons from '@theme/Icon';
|
||||
|
||||
# Subscribers
|
||||
|
||||
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.
|
||||
|
||||
Natively in Medusa there are subscribers to handle different events. However, you can also create your own custom subscribers.
|
||||
|
||||
Custom subscribers are TypeScript or JavaScript files in your project's `src/subscribers` directory. Files here should export classes, which will be treated as subscribers by Medusa. By convention, the class name should end with `Subscriber` and the file name should be the camel-case version of the class name without `Subscriber`. For example, the `WelcomeSubscriber` class is in the file `src/subscribers/welcome.ts`.
|
||||
|
||||
Whenever an event is emitted, the subscriber’s registered handler method is executed. The handler method receives as a parameter an object that holds data related to the event. For example, if an order is placed the `order.placed` event will be emitted and all the handlers will receive the order id in the parameter object.
|
||||
|
||||
### Example Use Cases
|
||||
|
||||
Subscribers are useful in many use cases, including:
|
||||
|
||||
- Send a confirmation email to the customer when they place an order by subscribing to the `order.placed` event.
|
||||
- Automatically assign new customers to a customer group by subscribing to the `customer.created`.
|
||||
- Handle custom events that you emit
|
||||
|
||||
---
|
||||
|
||||
## Custom Development
|
||||
|
||||
Developers can create custom subscribers in the Medusa backend, a plugin, or in a Commerce Module.
|
||||
|
||||
<DocCard item={{
|
||||
type: 'link',
|
||||
href: '/development/events/create-subscriber',
|
||||
label: 'Create a Subscriber',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to create a subscriber in Medusa.'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
Reference in New Issue
Block a user