Module Architecture
The core lifecycle, manifest configuration, and permission declarations for Medusa modules.
Every module in Medusa is defined by a main.js file located at the root of its directory inside modules/<name>/. This file exports a class that extends Modules from #medusa/modules.
The Module Class
Below is an example of a complete module declaration:
import { Modules } from "#medusa/modules";
export class Example extends Modules {
constructor(medusa) {
super(medusa, {
name: "example",
author: "Zero Development",
version: "1.0.0.0",
description: "An example module illustrating Medusa architecture."
});
}
permissions() {
return [
{
id: "example:manage",
name: "Example ( Manage )",
description: "Permission to manage the example module.",
group: "Example",
module: "example"
}
];
}
exampleHandler() {
return this.medusa.modules.handlers.get("example", "example");
}
async onStartup() {
this.medusa.console.log(this.medusa.console.levels.startup, `${this.getName()} is ready.`);
}
async onShutdown() {
this.medusa.console.log(this.medusa.console.levels.warning, `${this.getName()} has shut down.`);
}
}Manifest Properties
When calling super(medusa, metadata), the following properties must be supplied:
| Property | Type | Description |
|---|---|---|
name | string | The unique, lowercase identifier matching the module directory name (e.g. "moderation", "economy"). |
author | string | The author or development organization. |
version | string | The semver or four-part version string (e.g. "1.0.0.0"). |
description | string | A brief explanation of the module's role. |
The manifest name must remain lowercase. Display names in the dashboard are resolved through the localization key module.<id>.name in resources/lang/<code>/common.json.
Lifecycle Hooks
Medusa executes lifecycle hooks as the system transitions between states:
onStartup()
Called after the module's models, handlers, routes, commands, events, and clocks have been discovered and registered into the kernel. Use this hook to perform initializations, subscribe to custom events, or seed default database values.
onShutdown()
Called when Medusa receives a termination signal (SIGINT, SIGTERM), or when the module is dynamically unloaded. Use this hook to terminate active connections, clear intervals, or flush in-memory buffers.
Declaring Permissions
Modules declare role-based permissions through the permissions() method. These permissions appear automatically in the dashboard role management view:
permissions() {
return [
{
id: "ticket:manage",
name: "Tickets ( Manage )",
description: "Open, claim, transfer, and close support tickets.",
group: "Tickets",
module: "ticket"
}
];
}id: Unique identifier used to check permissions programmatically viathis.medusa.permissions.has(member, "ticket:manage").name: User-facing label displayed in dashboard role permission tables.description: Explains the authority granted by this permission.group: Category grouping in the dashboard UI.module: The module ID declaring this permission.

