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:
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):
| Option | Type | Description |
|---|---|---|
name | string | Unique identifier for this route. |
path | string | The Express route pattern (e.g. "/api/tickets/:id"). |
method | string | HTTP method: "get", "post", "put", "patch", or "delete". |
auth | boolean | Whether 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_SECRETRequests 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 });
}
