Разработка кастомного плагина Medusa.js

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка кастомного плагина Medusa.js
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Разработка кастомного плагина Medusa.js

В Medusa 2.x понятие «плагин» трансформировалось в Medusa Module — независимый пакет с собственными моделями, сервисами, миграциями и зависимостями. Плагины могут публиковаться в npm и подключаться в medusa-config.ts через modules[]. Это отличает подход от v1, где плагины были более монолитными.

Структура кастомного модуля/плагина

packages/my-module/
├── package.json
├── tsconfig.json
├── src/
│   ├── index.ts              # Точка входа модуля
│   ├── models/               # MikroORM сущности
│   │   └── custom-item.ts
│   ├── services/             # Бизнес-логика
│   │   └── custom-item.ts
│   ├── migrations/           # DB миграции
│   │   └── Migration20240101.ts
│   ├── loaders/              # Инициализация при старте
│   │   └── connection.ts
│   └── types/                # TypeScript типы
│       └── index.ts
└── dist/                     # Скомпилированный JS

Определение модели (MikroORM)

// src/models/custom-item.ts
import { model } from '@medusajs/framework/utils';

const CustomItem = model.define('custom_item', {
  id: model.id().primaryKey(),
  name: model.text(),
  sku: model.text().unique(),
  metadata: model.json().nullable(),
  is_active: model.boolean().default(true),
  sort_order: model.number().default(0),
  product_id: model.text().nullable(),
  created_at: model.dateTime(),
  updated_at: model.dateTime(),
});

export default CustomItem;

Сервис модуля

// src/services/custom-item.ts
import { MedusaService } from '@medusajs/framework/utils';
import CustomItem from '../models/custom-item';

class CustomItemModuleService extends MedusaService({
  CustomItem,
}) {
  // Кастомные методы поверх базовых CRUD
  async listActiveByProduct(productId: string) {
    return await this.listCustomItems({
      product_id: productId,
      is_active: true,
    }, {
      order: { sort_order: 'ASC' },
    });
  }

  async bulkUpdateSortOrder(items: Array<{ id: string; sort_order: number }>) {
    return await Promise.all(
      items.map(({ id, sort_order }) =>
        this.updateCustomItems({ id }, { sort_order })
      )
    );
  }
}

export default CustomItemModuleService;

Точка входа модуля

// src/index.ts
import { Module } from '@medusajs/framework/utils';
import CustomItemModuleService from './services/custom-item';

export const CUSTOM_ITEM_MODULE = 'customItem';

export default Module(CUSTOM_ITEM_MODULE, {
  service: CustomItemModuleService,
});

Подключение в medusa-config.ts

// medusa-config.ts
import { defineConfig } from '@medusajs/framework/config';
import CustomItemModule from './packages/my-module/src';

export default defineConfig({
  projectConfig: { /* ... */ },
  modules: [
    {
      resolve: './packages/my-module/src',
      // Или npm-пакет: resolve: 'medusa-module-custom-item'
      options: {
        apiEndpoint: process.env.CUSTOM_API_ENDPOINT,
        apiKey: process.env.CUSTOM_API_KEY,
      },
    },
  ],
});

API-роут для модуля

// src/api/admin/custom-items/route.ts
import type { MedusaRequest, MedusaResponse } from '@medusajs/framework/http';
import { CUSTOM_ITEM_MODULE } from '../../../modules/custom-item';
import type CustomItemModuleService from '../../../modules/custom-item/service';

export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
  const service: CustomItemModuleService = req.scope.resolve(CUSTOM_ITEM_MODULE);
  const [items, count] = await service.listAndCountCustomItems(
    {},
    { take: 20, skip: 0 }
  );
  res.json({ custom_items: items, count });
};

export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
  const service: CustomItemModuleService = req.scope.resolve(CUSTOM_ITEM_MODULE);
  const item = await service.createCustomItems(req.body);
  res.status(201).json({ custom_item: item });
};

Миграции

// src/migrations/Migration20240101120000.ts
import { Migration } from '@mikro-orm/migrations';

export class Migration20240101120000 extends Migration {
  async up(): Promise<void> {
    this.addSql(`
      CREATE TABLE IF NOT EXISTS "custom_item" (
        "id"         TEXT        NOT NULL,
        "name"       TEXT        NOT NULL,
        "sku"        TEXT        NOT NULL UNIQUE,
        "metadata"   JSONB,
        "is_active"  BOOLEAN     NOT NULL DEFAULT true,
        "sort_order" INTEGER     NOT NULL DEFAULT 0,
        "product_id" TEXT,
        "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
        "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
        CONSTRAINT "custom_item_pkey" PRIMARY KEY ("id")
      );
      CREATE INDEX idx_custom_item_product ON "custom_item" ("product_id");
    `);
  }

  async down(): Promise<void> {
    this.addSql(`DROP TABLE IF EXISTS "custom_item";`);
  }
}
# Применение миграций модуля
npx medusa db:migrate

Публикация как npm-пакет

{
  "name": "medusa-module-custom-item",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "prepublishOnly": "npm run build"
  },
  "peerDependencies": {
    "@medusajs/framework": "^2.0.0"
  },
  "keywords": ["medusa", "medusa-plugin", "ecommerce"]
}

Сроки разработки

  • Простой модуль с CRUD и API-роутами: 2–4 дня
  • Модуль с интеграцией внешнего API (loyalty, CRM, ERP): 5–10 дней
  • Сложный модуль с workflow, подписчиками, кастомными миграциями: 2–3 недели