Docs
Developer Documentation

Routes (REST API)

Expose custom HTTP endpoints through Medusa's internal Express REST API.

Medusa hosts an internal REST API server (port 3001) powered by Express. Modules can expose custom endpoints by placing classes extending Routes into src/routes/.

These endpoints are used by the web dashboard, webhook listeners, automated external services, and integration scripts.

Defining a Route

Each route file exports a class defining the HTTP path, method, and request handler:

modules/economy/src/routes/balance.js
import { Routes } from "#medusa/modules";

export class BalanceRoute extends Routes {
    constructor(medusa, module) {
        super(medusa, module, {
            name: "balance",
            path: "/api/economy/balance",
            method: "get"
        });
    }

    async handle(request, response) {
        const { userId, guildId } = request.query;
        if (!userId || !guildId) {
            response.status(400).json({ error: "Missing userId or guildId query parameter." });
            return;
        }

        const bank = this.medusa.modules.handlers.get(this.module, "bank");
        if (!bank) {
            response.status(503).json({ error: "Bank handler unavailable." });
            return;
        }

        const account = await bank.getBalance(userId, guildId);
        response.json({
            userId: account.userId,
            guildId: account.guildId,
            balance: Number(account.balance),
            bank: Number(account.bank)
        });
    }
}

Route Options

When calling super(medusa, module, options):

OptionTypeDescription
namestringUnique identifier for this route.
pathstringThe Express route pattern (e.g. "/api/tickets/:id").
methodstringHTTP method: "get", "post", "put", "patch", or "delete".
authbooleanWhether requests require the Authorization: Bearer <SECRET> header (default: true).

Authentication

By default, all routes are protected by Medusa's API security guard:

GET /api/economy/balance?userId=123&guildId=456 HTTP/1.1
Host: localhost:3001
Authorization: Bearer YOUR_AUTHENTICATION_SECRET

Requests omitting the bearer token or providing an invalid secret receive an immediate 401 Unauthorized response.

Error Handling

Routes should return clear error structures:

try {
    const result = await this.handler().performAction(request.body);
    response.json({ success: true, result });
} catch (error) {
    const status = error.status ? error.status : 500;
    response.status(status).json({ error: error.message });
}

On this page