Skip to main content

Build a Marketplace

This document guides you through the different documentation resources that will help you build a marketplace with Medusa.

Overview

A marketplace is an online commerce store that allows different vendors to sell their products within the same commerce system. Customers can purchase products from any of these vendors, and vendors can manage their orders separately.

Build a marketplaceCustomize the backend and handle events to build a marketplace.
Extend entities
Access logged-in user
Extend services
Handle events
Add Payment Provider
Create a storefront
Deploy the backend

Associate Entities with Stores

Entities represent tables in the database.

By default, entities like users, products, or orders aren't associated with a store, as it's assumed there's one store in Medusa. For a marketplace, each of these entities should be associated with their respective stores.

To associate these entities with the Store entity, you need to extend and customize entities created in the Medusa core package @medusajs/medusa, such as the User entity, to add a relation to the Store entity.

Example: Associate User with Store

For example, to associate the User entity with the Store entity, create the file src/models/user.ts with the following content:

src/models/user.ts
import { 
Column,
Entity,
Index,
JoinColumn,
ManyToOne,
} from "typeorm"
import {
User as MedusaUser,
} from "@medusajs/medusa"
import { Store } from "./store"

@Entity()
export class User extends MedusaUser {
@Index("UserStoreId")
@Column({ nullable: true })
store_id?: string

@ManyToOne(() => Store, (store) => store.members)
@JoinColumn({ name: "store_id", referencedColumnName: "id" })
store?: Store
}

Then, you need to extend the UserRepository to point to your extended entity. To do that, create the file src/repositories/user.ts with the following content:

src/repositories/user.ts
import { User } from "../models/user"
import {
dataSource,
} from "@medusajs/medusa/dist/loaders/database"
import {
UserRepository as MedusaUserRepository,
} from "@medusajs/medusa/dist/repositories/user"

export const UserRepository = dataSource
.getRepository(User)
.extend({
...Object.assign(
MedusaUserRepository,
{ target: User }
),
})

export default UserRepository

Next, you need to create a migration that reflects the changes on the User entity in your database. To do that, run the following command to create a migration file:

npx typeorm migration:create src/migrations/add-user-store-id

This creates a file in the src/migrations directory of the format <TIMESTAMP>_add-user-store-id.ts. Replace the up and down methods in that file with the methods here:

src/migrations/<TIMESTAMP>_add-user-store-id.ts
// ...

export class AddUserStoreId1681287255173
implements MigrationInterface {
// ...

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "user" ADD "store_id" character varying`
)
await queryRunner.query(
`CREATE INDEX "UserStoreId" ON "user" ("store_id")`
)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`DROP INDEX "public"."UserStoreId"`
)
await queryRunner.query(
`ALTER TABLE "user" DROP COLUMN "store_id"`
)
}

}

Finally, to reflect these changes and start using them, build your changes and run migrations with the following commands:

npm run build
npx @medusajs/medusa migrations run

You can extend other entities in a similar manner to associate them with a store.


Accessing Logged-in User

Throughout your development, you'll likely need access to the logged-in user. For example, you'll need to know which user is logged in to know which store to associate a new product with.


Customize Data Management Functionalities

After associating entities with stores, you'll need to customize how certain data management functionalities are implemented in the Medusa core package.

For example, when a new user is created, you need to ensure that it's associated either with a new store or with the store of the logged-in user. Another example is associating a new product with the logged-in user's store.

You can customize these functionalities by extending services. Services are classes that contain helper methods specific to an entity. For example, the UserService is used to manage functionalities related to the User entity, such as creating a user.

You can also extend services if you need to customize a functionality implemented in a service for other reasons.

Example: Extend User Service

You can extend the user service to change how the create method is implemented.

To extend the user service, create the file src/services/user.ts with the following content:

import { Lifetime } from "awilix"
import {
UserService as MedusaUserService,
} from "@medusajs/medusa"
import { User } from "../models/user"
import {
CreateUserInput as MedusaCreateUserInput,
} from "@medusajs/medusa/dist/types/user"
import StoreRepository from "../repositories/store"

type CreateUserInput = {
store_id?: string
} & MedusaCreateUserInput

class UserService extends MedusaUserService {
static LIFE_TIME = Lifetime.SCOPED
protected readonly loggedInUser_: User | null
protected readonly storeRepository_: typeof StoreRepository

constructor(container, options) {
super(...arguments)
this.storeRepository_ = container.storeRepository

try {
this.loggedInUser_ = container.loggedInUser
} catch (e) {
// avoid errors when backend first runs
}
}

async create(
user: CreateUserInput,
password: string
): Promise<User> {
if (!user.store_id) {
const storeRepo = this.manager_.withRepository(
this.storeRepository_
)
let newStore = storeRepo.create()
newStore = await storeRepo.save(newStore)
user.store_id = newStore.id
}

return await super.create(user, password)
}
}

export default UserService

In the create method of this extended service, you create a new store if the user being created doesn't have a store associated with it.

You can then test out your customization by running the build command and starting the backend:

npm run build
npx @medusajs/medusa develop

Listening to Events

While implementing your marketplace, you'll typically need to listen to certain events then perform actions asynchronously. For example, you can listen to the order.placed event and, when triggered, create child orders of the order, separating ordered items by their associated store.

To listen to events, you need to create Subscribers that subscribe a handler method to an event. In that handler method, you can implement the desired functionality.

Example: Listen to Order Created Event

To listen to the order.placed event, create the file src/subscribers/orderNotifier.ts with the following content:

class OrderNotifierSubscriber {
constructor({ eventBusService }) {
eventBusService.subscribe("order.placed", this.handleOrder)
}

handleOrder = async (data) => {
// TODO perform functionality
}
}

export default OrderNotifierSubscriber

This subscribes the handleOrder method to be executed whenever the order.placed event is emitted.

You can then test out your subscriber by running the build command and starting the backend:

npm run build
npx @medusajs/medusa develop

Add Payment and Fulfillment Providers

Payment and fulfillment providers can be added through plugins or directly in your project. You can either create your own provider, use one of Medusa's official plugins, or use community plugins.

Payment and fulfillment providers are associated with regions, which are not associated with a store, by default. If you want to allow each store to specify its own payment and fulfillment providers, you'll need to associate the region with a store.

Option 1: Create your own providers

Option 2: Install a Plugin


Build a Storefront

Medusa provides a Next.js starter storefront that you can use with Medusa. Since you've customized your Medusa project, you'll need to either customize the existing Next.js storefront, or create a custom storefront.


Deploy Marketplace

Our documentation includes deployment guides for a basic Medusa backend. You should be able to follow it to deploy your customized marketplace, as well.


Additional Development

You can find other resources for your marketplace development in the Medusa Development section of this documentation.

Was this page helpful?