# Handlers

URL: https://zerodev.ca/docs/medusa/developer-documentation/handlers

> Encapsulate business logic, orchestrate models, and share workflows across commands and routes.



Handlers encapsulate business logic and data workflows. Rather than embedding database mutations or Discord interactions directly into slash commands or HTTP routes, all core logic resides in handlers. Commands and routes act as thin dispatch layers that parse input and delegate execution to handlers.

Handlers reside in `src/handlers/` and load from every `*.js` file located directly in that directory.

## Base Class Definition

```js title="modules/example/src/handlers/Items.js"
import { Handlers } from "#medusa/modules";

export class ItemsHandler extends Handlers {
    constructor(medusa) {
        super(medusa, {
            name: "items"
        });
    }

    async create(guildId, userId, identifier, name, options = {}) {
        if (!identifier) this.fail(400, "An item identifier is required.");
        if (!name) this.fail(400, "An item name is required.");

        const existing = await this.medusa.modules.models.get(this.module, "example_items").get({ identifier });
        if (existing) this.fail(409, `An item with identifier "${identifier}" already exists.`);

        return this.medusa.modules.models.get(this.module, "example_items").create({
            guildId,
            userId,
            identifier,
            name,
            ...options
        });
    }

    async getByIdentifier(identifier) {
        const item = await this.medusa.modules.models.get(this.module, "example_items").get({ identifier });
        if (!item) this.fail(404, `Item "${identifier}" was not found.`);
        return item;
    }

    async listForGuild(guildId) {
        return this.medusa.modules.models.get(this.module, "example_items").list({ guildId });
    }

    async remove(identifier) {
        const deleted = await this.medusa.modules.models.get(this.module, "example_items").delete({ identifier });
        if (!deleted) this.fail(404, `Item "${identifier}" was not found.`);
        return { success: true };
    }
}
```

## Built-In Methods

The `Handlers` base class provides the following core methods:

| Method        | Signature                                    | Description                                                                                                                                                                                             |
| ------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `getName()`   | `() => string`                               | Returns the handler name identifier.                                                                                                                                                                    |
| `this.fail()` | `(status: number, message: string) => never` | Throws an Error with an attached HTTP `status` property. When thrown during an HTTP route execution, Medusa catches the error and sends the corresponding HTTP response code with `{ error: message }`. |

## Error Bubbling with `this.fail()`

The `fail()` method allows one-line error throwing that integrates cleanly with both HTTP routes and Discord interactions:

```js
if (amount <= 0) {
    this.fail(400, "Amount must be greater than zero.");
}
```

When called from an HTTP route, Medusa automatically catches the thrown error and responds with the matching HTTP status code:

```js
async GET(request) {
    const handler = this.medusa.modules.handlers.get(this.module, "items");
    return handler.getByIdentifier(request.query.identifier);
}
```

When called from a Discord slash command, wrap execution in a try-catch block to render an error embed:

```js
try {
    await handler.create(interaction.guild.id, interaction.user.id, id, name);
    return interaction.editReply({
        embeds: [this.medusa.embeds.success("Item created successfully.")]
    });
} catch (error) {
    return interaction.editReply({
        embeds: [this.medusa.embeds.error(error.message)]
    });
}
```

## Cross-Module Handler Access

Modules frequently orchestrate actions implemented by other modules. Handlers are queried dynamically through `this.medusa.modules.handlers.get(moduleName, handlerName)`:

```js
const economy = this.medusa.modules.handlers.get("economy", "balances");
if (economy) {
    await economy.add(guildId, userId, 100, "wallet");
}
```

If the requested module is disabled by server administrators or not installed, `get()` returns `null`. Always check for existence before invoking methods on external handlers.

## Interfacing with Core Utilities

Handlers have access to all root Medusa utilities via `this.medusa`:

```js
async completePurchase(guildId, user, item) {
    await this.medusa.audit.record({
        type: "module",
        actorId: user.id,
        actorTag: user.tag,
        command: "item:purchase",
        success: true,
        detail: { item: item.identifier, price: item.price },
        guildId
    });

    await this.medusa.logs.post(this.module, "itemPurchased", {
        actor: user,
        subject: item.name,
        fields: [
            { name: "Price", value: `$${item.price}`, inline: true },
            { name: "Identifier", value: item.identifier, inline: true }
        ]
    });
}
```
