Framework

Framework
Build any feature,
integrate with any system

With the Medusa framework, the only limit is your imagination.
- Sync product data from ERP
- Inject custom price data to cart
- Extend data schemas
- Add custom endpoints
- Hook into existing workflows
- Manage complex storefront queries
- Build custom data models
- Listen to webhooks from 3rd-party tools
- Create advanced automations
- Add human-in-the-loop workflows
Workflows
Orchestrate actions across systems with Workflows
Traditional commerce platforms force you to use separate servers, middleware, or applications for customizations. This is hard to manage, leading to long implementations and unreliable operations. In Medusa, you have a built-in foundation to build customizations natively.
Our tools enable you to avoid hacky workarounds and instead build bespoke, scalable commerce applications fast. Let Medusa provide the framework, so you can focus on building the features that differentiate your business.
create-order-in-erp.ts
sync-order-to-erp.ts
A workflow step performs a query or action in a system. Steps can receive inputs from previous steps and return data to be used in subsequent steps. You can resolve services in your steps allowing you to build business logic involving Medusa’s core commerce modules or your 3rd party or custom systems.
To connect multiple steps together you create a Workflow. Creating a Workflow is like writing any other function. Behind the scenes Medusa generates a representation of your workflow that enables automatic retries of steps if they fail.
If step retries don't succeed, Medusa will rollback previous steps. A rollback will call a step's compensation method, ensuring your data is kept consistent across systems.
You can trigger your workflows anywhere in Medusa. Use your workflows in Subscribers to start them in response to an event. Start them in response to requests with API Routes. Or have them run on a defined schedule with Scheduled Jobs.
src > workflows > steps > create-order-in-erp.ts
1
A workflow step performs a query or action in a system. Steps can receive inputs from previous steps and return data to be used in subsequent steps. You can resolve services in your steps allowing you to build business logic involving Medusa’s core commerce modules or your 3rd party or custom systems.
create-order-in-erp.ts
sync-order-to-erp.ts
src > workflows > steps > create-order-in-erp.ts
123456789101112import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"export const createOrderInERP = createStep("create-order-in-erp"async (order: StepInput, { container }) => {const erpService = container.resolve("erp")const createdOrder = await erpService.createOrder(order)return new StepResponse(createdOrder)})
To connect multiple steps together you create a Workflow. Creating a Workflow is like writing any other function. Behind the scenes Medusa generates a representation of your workflow that enables automatic retries of steps if they fail.
create-order-in-erp.ts
sync-order-to-erp.ts
src > workflows > sync-order-to-erp.ts
1234567891011121314151617181920import { createWorkflow, WorkflowResponse } from "@medusajs/framework/workflows-sdk"import { createOrderInERP, transformOrder } from "./steps"export const syncOrderToERP = createWorkflow("sync-order-to-erp",(input: WorkflowInput, { container }) => {const medusaOrder = retrieveOrder(input.order_id)const createdOrder = when({ medusaOrder }, ({ medusaOrder }) => {return medusaOrder.status === "completed"}).then(() => {const preparedOrder = transformOrder(medusaOrder)return createOrderInERP(preparedOrder)})return new WorkflowResponse(createdOrder)})
If step retries don't succeed, Medusa will rollback previous steps. A rollback will call a step's compensation method, ensuring your data is kept consistent across systems.
create-order-in-erp.ts
sync-order-to-erp.ts
src > workflows > steps > create-order-in-erp.ts
123456789101112131415161718import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"export const createOrderInERP = createStep("create-order-in-erp"async (order: StepInput, { container }) => {const erpService = container.resolve("erp")const createdOrder = await erpService.createOrder(order)return new StepResponse(createdOrder, createdOrder)},// In the face of errors, we roll back and delete the orderasync (createdOrder, { container }) => {const erpService = container.resolve("erp")await erpService.deleteOrder(createdOrder)})
You can trigger your workflows anywhere in Medusa. Use your workflows in Subscribers to start them in response to an event. Start them in response to requests with API Routes. Or have them run on a defined schedule with Scheduled Jobs.
create-order-in-erp.ts
sync-order-to-erp.ts
src > subscribers > sync-order-to-erp.ts
123456789101112131415161718192021222324import {type SubscriberConfig,} from "@medusajs/framework"import syncOrderToErp from "../workflows/sync-order-to-erp"export default async function handleOrderPlaced({event: { data },container,}) {const orderService = container.resolve("order")const order = await orderService.retrieve(id)await syncOrderToErp(container).run({input: {order},})}export const config: SubscriberConfig = {event: "order.placed",}
Modules
Build custom modules to support any commerce logic
Unlike other platforms that require separate applications for customization, Medusa enables you to write custom modules that integrate seamlessly into your application.
You can define your own data models, endpoints, and logic for modules that work alongside Medusa's pre-built commerce modules.
store.medusajs.com
company.ts
service.ts
create-company.ts
route.ts
A data model represents a table in the database. You create data models using Medusa's Data Model API. It simplifies defining a table's columns, relations, and indexes with straightforward methods and configurations.
You perform database operations on your data models in a service. Services provide methods, such as create, update, retrieve, list, and more, to manage your data models out of the box. Additionally, services can hold custom methods for your business logic and/or integrate with third-party providers.
Once services are defined, they can be resolved from Medusa's dependency container in API Routes, Subscribers, Scheduled Jobs, Workflows, and more.
With multiple modules defined and linked to each other, you can use Query to retrieve data across all of them, including third-party systems. Similar to services, Query can be resolved from Medusa's dependency container across all the different application entry points.
src > modules > company > models > company.ts
1
A data model represents a table in the database. You create data models using Medusa's Data Model API. It simplifies defining a table's columns, relations, and indexes with straightforward methods and configurations.
company.ts
service.ts
create-company.ts
route.ts
src > modules > company > models > company.ts
123456789101112131415161718192021import { model } from "@medusajs/framework/utils"export const Company = model.define("company", {id: model.id({ prefix: "comp" }).primaryKey(),name: model.text(),logo_url: model.text().nullable(),domain: model.text().nullable(),employees: model.hasMany(() => Employee),spending_limit_reset_frequency: model.enum(["never", "daily", "weekly", "monthly", "yearly"]).default("monthly"),})export const Employee = model.define("employee", {id: model.id({ prefix: "emp" }).primaryKey(),email: model.text(),spending_limit: model.bigNumber().default(0),company: model.belongsTo(() => Company, {mappedBy: "employees",}),});
You perform database operations on your data models in a service. Services provide methods, such as create, update, retrieve, list, and more, to manage your data models out of the box. Additionally, services can hold custom methods for your business logic and/or integrate with third-party providers.
company.ts
service.ts
create-company.ts
route.ts
src > modules > company > service.ts
12345678910111213141516import { MedusaService } from "@medusajs/framework/utils"import { Company, Employee } from "./models"class CompanyModuleService extends MedusaService({Company,Employee}) {async validateEmployeeEmail(employee, company) {if (!employee.email.includes(company.domain)) {throw new Error("Employee email not associated with company")}}}export default CompanyModuleService
Once services are defined, they can be resolved from Medusa's dependency container in API Routes, Subscribers, Scheduled Jobs, Workflows, and more.
company.ts
service.ts
create-company.ts
route.ts
src > workflows > steps > companies > create-company.ts
123456789101112import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"export const createCompanyStep = createStep("create-company-step",async (input: CreateCompanyStepInput, { container }) => {const companyService = container.resolve("company")const company = await companyService.createCompanies(input)return new StepResponse(company)})
With multiple modules defined and linked to each other, you can use Query to retrieve data across all of them, including third-party systems. Similar to services, Query can be resolved from Medusa's dependency container across all the different application entry points.
company.ts
service.ts
create-company.ts
route.ts
src > api > admin > companies > route.ts
12345678910111213141516171819import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"export const POST = async (req: MedusaRequest,res: MedusaResponse) => {const query = req.scope.resolve("query")const { data } = await query({entity: "company",fields: ["id", "name", "employees"],filters: {take: 20,skip: 0}})res.json({ companies: data })}
Query
Query data across multiple systems.
Query gives you a single entry point to data across all systems in your stack. Create data relationships between modules with the new Link Modules and write queries that fetch data from multiple sources, including 3rd-party systems.

Admin extensions
Powerful system for Admin extensions.
Extend existing Admin pages by adding widgets, which let you insert custom React components into predefined injection zones. Or go all the way and create new bespoke Admin pages using UI routes.
erp-button.tsx
page.tsx
Widgets allow you to add custom React components in injection zones spread across existing pages in the Admin Dashboard. For example, you can add a button on the order details page that links to the order in an integrated ERP system. Widgets are easy to build, and Medusa automatically registers them when exporting a React component and its configuration from files placed in your Medusa project.
UI Routes allow you to add new routes to your admin dashboard. Similar to Widgets, Admin UI Routes are simply exported React components created in files in your Medusa project. UI Routes can be added to the sidebar as new items or sub-items of existing routes.
src > admin > widgets > erp-button.tsx
1
Widgets allow you to add custom React components in injection zones spread across existing pages in the Admin Dashboard. For example, you can add a button on the order details page that links to the order in an integrated ERP system. Widgets are easy to build, and Medusa automatically registers them when exporting a React component and its configuration from files placed in your Medusa project.
erp-button.tsx
page.tsx
src > admin > widgets > erp-button.tsx
1234567891011121314151617181920import type { WidgetConfig } from "@medusajs/admin"import { Container } from "@medusajs/ui"const OrderWidget = ({ order }) => {const link = `https://myerp.com/orders/${order.id}`return (<Container className="divide-y p-0"><a href={link}>View in MyERP</a></Container>)}export const config = defineWidgetConfig({zone: "order.details.before",})export default OrderWidget
UI Routes allow you to add new routes to your admin dashboard. Similar to Widgets, Admin UI Routes are simply exported React components created in files in your Medusa project. UI Routes can be added to the sidebar as new items or sub-items of existing routes.
erp-button.tsx
page.tsx
src > admin > routes > erp-orders > page.tsx
12345678910111213141516171819import { defineRouteConfig } from "@medusajs/admin-sdk"import { Container, Heading } from "@medusajs/ui"const ERPOrdersPage = () => {return (<Container className="divide-y p-0"><div className="flex items-center justify-between px-6 py-4"><Heading level="h1">Nested Orders Page</Heading></div></Container>)}export const config = defineRouteConfig({label: "ERP Orders",nested: "/orders",})export default ERPOrdersPage
Admin extensions
Powerful system for Admin extensions and customizations.
Support your custom logic and unique business workflows with a powerful extension system and native UI.



Recipes
Build any commerce application.
Want to get started but not sure where to begin? Use some of our recipes to build your own custom use case

Marketplace
Multi-vendor marketplace.

B2B
Business-to-business commerce.

Commerce Automation
Automated commerce operations.

Digital Products
Downloadable products.

Ecommerce
Traditional ecommerce store.

Integrate Ecommerce stack
Multi-system commerce setups.

Multi-Region Store
Global currencies, taxes, and more.

Omnichannel Store
Multiple sales channels.

OMS
Order Management System.

Personalized Products
Custom product configurations.

POS
Backend for Point-of-Sales.

Subscriptions
Recurring product purchases.