Handlers
Encapsulate business logic, orchestrate models, and enable cross-module communication.
In Medusa, Handlers are the single home for all business logic. Commands, REST API routes, gateway events, and scheduled clocks should never write directly to database models or perform complex orchestration themselves. Instead, they delegate to handlers.
All handlers live in src/handlers/ inside their respective module folder.
Defining a Handler
Handlers extend the Handlers base class exported from #medusa/modules:
import { Handlers } from "#medusa/modules";
export class Bank extends Handlers {
constructor(medusa, module) {
super(medusa, module, { name: "bank" });
}
model() {
return this.medusa.modules.models.get(this.module, "Accounts");
}
async getBalance(userId, guildId) {
const account = await this.model().findOrCreate({
where: { userId, guildId },
defaults: { balance: 0, bank: 0 }
});
return account[0];
}
async deposit(userId, guildId, amount) {
if (amount <= 0) throw Object.assign(new Error("Deposit amount must be positive."), { status: 400 });
const account = await this.getBalance(userId, guildId);
if (account.balance < amount) throw Object.assign(new Error("Insufficient wallet balance."), { status: 400 });
account.balance -= amount;
account.bank += amount;
await account.save();
return account;
}
}Cross-Module Communication
One of Medusa's core strengths is modular composability. A handler in one module can invoke methods from another module without tight coupling:
const economy = this.medusa.modules.handlers.get("economy", "bank");
if (economy) {
await economy.deposit(member.id, guild.id, 100);
}If the requested module is disabled or uninstalled, handlers.get() returns null, allowing your code to gracefully handle optional dependencies.
Key Properties & Methods
Inside any handler class:
this.medusa: The root Medusa instance.this.module: The string identifier of the module owning this handler (e.g."economy").this.medusa.modules.models.get(module, name): Retrieves a Sequelize model registered by any module.this.medusa.audit.record(...): Appends an entry to the system audit trail.this.medusa.locales.resolve(key, locale, replacements): Resolves a localized phrase from the language store.

