- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
2.4.2. Guide: Sync Brands from Medusa to CMS
In the previous chapter, you created a CMS Module that integrates a dummy third-party system. You can now perform actions using that module within your custom flows.
In another previous chapter, you added a workflow that creates a brand. After integrating the CMS, you want to sync that brand to the third-party system as well.
Medusa has an event system that emits events when an operation is performed. It allows you to listen to those events and perform an asynchronous action in a function called a subscriber. This is useful to perform actions that aren't integral to the original flow, such as syncing data to a third-party system.
In this chapter, you'll modify the createBrandWorkflow
you created before to emit a custom event that indicates a brand was created. Then, you'll listen to that event in a subscriber to sync the brand to the third-party CMS. You'll implement the sync logic within a workflow that you execute in the subscriber.
1. Emit Event in createBrandWorkflow#
Since syncing the brand to the third-party system isn't integral to creating a brand, you'll emit a custom event indicating that a brand was created.
Medusa provides an emitEventStep
that allows you to emit an event in your workflows. So, in the createBrandWorkflow
defined in src/workflows/create-brand.ts
, use the emitEventStep
helper step after the createBrandStep
:
1// other imports...2import { 3 emitEventStep,4} from "@medusajs/medusa/core-flows"5 6// ...7 8export const createBrandWorkflow = createWorkflow(9 "create-brand",10 (input: CreateBrandInput) => {11 // ...12 13 emitEventStep({14 eventName: "brand.created",15 data: {16 id: brand.id,17 },18 })19 20 return new WorkflowResponse(brand)21 }22)
The emitEventStep
accepts an object parameter having two properties:
eventName
: The name of the event to emit. You'll use this name later to listen to the event in a subscriber.data
: The data payload to emit with the event. This data is passed to subscribers that listen to the event. You add the brand's ID to the data payload, informing the subscribers which brand was created.
You'll learn how to handle this event in a later step.
2. Create Sync to Third-Party System Workflow#
The subscriber that will listen to the brand.created
event will sync the created brand to the third-party CMS. So, you'll implement the syncing logic in a workflow, then execute the workflow in the subscriber.
Workflows have a built-in durable execution engine that helps you complete tasks spanning multiple systems. Also, their rollback mechanism ensures that data is consistent across systems even when errors occur during execution.
You'll create a syncBrandToSystemWorkflow
that has two steps:
useQueryGraphStep
: a step that Medusa provides to retrieve data using Query. You'll use this to retrieve the brand's details using its ID.syncBrandToCmsStep
: a step that you'll create to sync the brand to the CMS.
syncBrandToCmsStep#
To implement the step that syncs the brand to the CMS, create the file src/workflows/sync-brands-to-cms.ts
with the following content:
5import CmsModuleService from "../modules/cms/service"6 7type SyncBrandToCmsStepInput = {8 brand: InferTypeOf<typeof Brand>9}10 11const syncBrandToCmsStep = createStep(12 "sync-brand-to-cms",13 async ({ brand }: SyncBrandToCmsStepInput, { container }) => {14 const cmsModuleService: CmsModuleService = container.resolve(CMS_MODULE)15 16 await cmsModuleService.createBrand(brand)17 18 return new StepResponse(null, brand.id)19 },20 async (id, { container }) => {21 if (!id) {22 return23 }24 25 const cmsModuleService: CmsModuleService = container.resolve(CMS_MODULE)26 27 await cmsModuleService.deleteBrand(id)28 }29)
You create the syncBrandToCmsStep
that accepts a brand as an input. In the step, you resolve the CMS Module's service from the Medusa container and use its createBrand
method. This method will create the brand in the third-party CMS.
You also pass the brand's ID to the step's compensation function. In this function, you delete the brand in the third-party CMS if an error occurs during the workflow's execution.
Create Workflow#
You can now create the workflow that uses the above step. Add the workflow to the same src/workflows/sync-brands-to-cms.ts
file:
1// other imports...2import { 3 // ...4 createWorkflow, 5 WorkflowResponse,6} from "@medusajs/framework/workflows-sdk"7import { useQueryGraphStep } from "@medusajs/medusa/core-flows"8 9// ...10 11type SyncBrandToCmsWorkflowInput = {12 id: string13}14 15export const syncBrandToCmsWorkflow = createWorkflow(16 "sync-brand-to-cms",17 (input: SyncBrandToCmsWorkflowInput) => {18 // @ts-ignore19 const { data: brands } = useQueryGraphStep({20 entity: "brand",21 fields: ["*"],22 filters: {23 id: input.id,24 },25 options: {26 throwIfKeyNotFound: true,27 },28 })29 30 syncBrandToCmsStep({31 brand: brands[0],32 } as SyncBrandToCmsStepInput)33 34 return new WorkflowResponse({})35 }36)
You create a syncBrandToCmsWorkflow
that accepts the brand's ID as input. The workflow has the following steps:
useQueryGraphStep
: Retrieve the brand's details using Query. You pass the brand's ID as a filter, and set thethrowIfKeyNotFound
option to true so that the step throws an error if a brand with the specified ID doesn't exist.syncBrandToCmsStep
: Create the brand in the third-party CMS.
You'll execute this workflow in the subscriber next.
3. Handle brand.created Event#
You now have a workflow with the logic to sync a brand to the CMS. You need to execute this workflow whenever the brand.created
event is emitted. So, you'll create a subscriber that listens to and handle the event.
Subscribers are created in a TypeScript or JavaScript file under the src/subscribers
directory. So, create the file src/subscribers/brand-created.ts
with the following content:
1import type {2 SubscriberConfig,3 SubscriberArgs,4} from "@medusajs/framework"5import { syncBrandToCmsWorkflow } from "../workflows/sync-brands-to-cms"6 7export default async function brandCreatedHandler({8 event: { data },9 container,10}: SubscriberArgs<{ id: string }>) {11 await syncBrandToCmsWorkflow(container).run({12 input: data,13 })14}15 16export const config: SubscriberConfig = {17 event: "brand.created",18}
A subscriber file must export:
- The asynchronous function that's executed when the event is emitted. This must be the file's default export.
- An object that holds the subscriber's configurations. It has an
event
property that indicates the name of the event that the subscriber is listening to.
The subscriber function accepts an object parameter that has two properties:
event
: An object of event details. Itsdata
property holds the event's data payload, which is the brand's ID.container
: The Medusa container used to resolve framework and commerce tools.
In the function, you execute the syncBrandToCmsWorkflow
, passing it the data payload as an input. So, everytime a brand is created, Medusa will execute this function, which in turn executes the workflow to sync the brand to the CMS.
Test it Out#
To test the subscriber and workflow out, you'll use the Create Brand API route you created in a previous chapter.
First, start the Medusa application:
Since the /admin/brands
API route has a /admin
prefix, it's only accessible by authenticated admin users. So, to retrieve an authenticated token of your admin user, send a POST
request to the /auth/user/emailpass
API Route:
Make sure to replace the email and password with your admin user's credentials.
Then, send a POST
request to /admin/brands
, passing the token received from the previous request in the Authorization
header:
This request returns the created brand. If you check the logs, you'll find the brand.created
event was emitted, and that the request to the third-party system was simulated:
1info: Processing brand.created which has 1 subscribers2http: POST /admin/brands ← - (200) - 16.418 ms3info: Sending a POST request to /brands.4info: Request Data: {5 "id": "01JEDWENYD361P664WRQPMC3J8",6 "name": "Acme",7 "created_at": "2024-12-06T11:42:32.909Z",8 "updated_at": "2024-12-06T11:42:32.909Z",9 "deleted_at": null10}11info: API Key: "123"
Next Chapter: Sync Brand from Third-Party CMS to Medusa#
You can also automate syncing data from a third-party system to Medusa at a regular interval. In the next chapter, you'll learn how to sync brands from the third-party CMS to Medusa once a day.