Docs
Developer Documentation

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:

modules/example/main.js
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:

PropertyTypeDescription
namestringThe unique, lowercase identifier matching the module directory name (e.g. "moderation", "economy").
authorstringThe author or development organization.
versionstringThe semver or four-part version string (e.g. "1.0.0.0").
descriptionstringA 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 via this.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.

On this page