# Документация API Справочник эндпоинтов для бэкофиса маркетплейса. Базовый URL: `https://your-api-domain.com/api` > 🇬🇧 English documentation: [API.md](./API.md) --- ## Проекты ### Получить все проекты ``` GET /api/projects Ответ 200: [ { "id": "dexar", "name": "dexar", "displayName": "Dexar Marketplace", "active": true, "logoUrl": "https://..." } ] ``` --- ## Категории ### Получить категории проекта ``` GET /api/projects/:projectId/categories Ответ 200: [ { "id": "cat1", "name": "Электроника", "visible": true, "priority": 1, "img": "https://...", "projectId": "dexar", "subcategories": [ ...Subcategory[] ], "translations": { "ru": { "name": "Электроника" } } } ] ``` ### Получить категорию по ID ``` GET /api/categories/:categoryId Ответ 200: (объект категории с вложенными подкатегориями) ``` ### Создать категорию ``` POST /api/projects/:projectId/categories Тело запроса: { "name": "Новая категория", // обязательно "visible": true, "priority": 10, "img": "https://...", "translations": { // опционально "ru": { "name": "Новая категория" } } } Ответ 201: (созданный объект категории) ``` ### Обновить категорию ``` PATCH /api/categories/:categoryId Тело запроса: (любое подмножество полей) { "name": "Обновлённое название", "visible": false, "priority": 5, "translations": { "ru": { "name": "Обновлённое название" } } } Ответ 200: (обновлённый объект категории) ``` ### Удалить категорию ``` DELETE /api/categories/:categoryId Ответ 204 No Content Примечание: каскадно удаляет все подкатегории и товары внутри ``` --- ## Подкатегории Подкатегории **рекурсивны** — вложенность неограничена. Единственное ограничение: **подкатегория с товарами не может иметь дочерних подкатегорий** (и наоборот). ### Объект подкатегории ```json { "id": "sub1", "name": "Смартфоны", "visible": true, "priority": 1, "img": "https://...", "categoryId": "cat1", // всегда ID корневой категории "parentId": "cat1", // ID прямого родителя (категория или подкатегория) "itemCount": 15, "hasItems": true, "subcategories": [], // дочерние подкатегории (пусто, если hasItems = true) "translations": { "ru": { "name": "Смартфоны" } } } ``` > `categoryId` — всегда ID **корневой категории** этого поддерева. > `parentId` — ID **прямого родителя**: может быть ID категории или подкатегории. --- ### Получить подкатегории категории ``` GET /api/categories/:categoryId/subcategories Ответ 200: Subcategory[] (вложенные подкатегории рекурсивно заполнены) ``` ### Получить подкатегорию по ID ``` GET /api/subcategories/:subcategoryId Ответ 200: объект подкатегории (с вложенными подкатегориями при наличии) ``` ### Создать подкатегорию в категории (уровень 1) ``` POST /api/categories/:categoryId/subcategories Тело запроса: { "id": "custom-id", // опционально, генерируется автоматически (используется в URL) "name": "Смартфоны", // обязательно "visible": true, "priority": 10, "translations": { "ru": { "name": "Смартфоны" } } } Ответ 201: (созданный объект подкатегории) Ошибка 400: если категория не существует ``` ### Создать подкатегорию в подкатегории (уровень 2+, вложенная) ``` POST /api/subcategories/:parentSubcategoryId/subcategories Тело запроса: { "id": "custom-id", // опционально "name": "Apple", // обязательно "visible": true, "priority": 10 } Ответ 201: (созданный объект подкатегории) Ошибка 400: если родительская подкатегория содержит товары (hasItems = true) Ошибка 404: если родительская подкатегория не найдена ``` ### Обновить подкатегорию ``` PATCH /api/subcategories/:subcategoryId Тело запроса: (любое подмножество полей) { "id": "new-slug", // ID редактируется — используется в URL маркетплейса "name": "Новое название", "visible": false, "priority": 3, "translations": { "ru": { "name": "Новое название" } } } Ответ 200: (обновлённый объект подкатегории) ``` ### Удалить подкатегорию ``` DELETE /api/subcategories/:subcategoryId Ответ 204 No Content Примечание: каскадно удаляет все вложенные подкатегории и товары ``` --- ## Товары Товары всегда принадлежат **самой глубокой подкатегории** в иерархии (листовой узел). Подкатегория с хотя бы одним товаром имеет `hasItems: true` и не может принимать дочерние подкатегории. ### Получить товары (с пагинацией) ``` GET /api/subcategories/:subcategoryId/items Query-параметры: page number (по умолчанию: 1) limit number (по умолчанию: 20) search string опционально — фильтр по названию (без учёта регистра) visible boolean опционально — фильтр по видимости tags string опционально — теги через запятую lang string опционально — код языка (en | ru); влияет на поля names/descriptions в ответе Ответ 200: { "items": [ { "id": "item1", "name": "iPhone 15 Pro", "visible": true, "priority": 1, "quantity": 50, "price": 1299, "currency": "USD", "imgs": ["https://...", "https://..."], "tags": ["new", "featured"], "badges": ["new", "exclusive"], "simpleDescription": "Последний iPhone...", "description": [ { "key": "Цвет", "value": "Чёрный" }, { "key": "Память", "value": "256 ГБ" } ], "subcategoryId": "sub1", "translations": { "ru": { "name": "iPhone 15 Pro", "simpleDescription": "Последний iPhone с корпусом из титана", "description": [ { "key": "Цвет", "value": "Чёрный" }, { "key": "Память", "value": "256 ГБ" } ] } }, "comments": [ { "id": "c1", "text": "Отличный товар!", "author": "Иван", "stars": 5, "createdAt": "2024-01-10T10:30:00Z" } ] } ], "total": 150, "page": 1, "limit": 20, "hasMore": true } ``` ### Получить товар по ID ``` GET /api/items/:itemId Query-параметры: lang string опционально (en | ru) Ответ 200: (полный объект товара) ``` ### Создать товар ``` POST /api/subcategories/:subcategoryId/items Тело запроса: { "name": "Новый товар", // обязательно "visible": true, "priority": 10, "quantity": 100, "price": 999, "currency": "USD", // USD | EUR | RUB | GBP | UAH "imgs": ["https://..."], "tags": ["new"], "badges": ["new", "exclusive"], // опционально — стандартные или свои метки "simpleDescription": "Краткое описание", "description": [ { "key": "Размер", "value": "Большой" } ], "translations": { // опционально "ru": { "name": "Новый товар", "simpleDescription": "Краткое описание", "description": [ { "key": "Размер", "value": "Большой" } ] } } } Ответ 201: (созданный объект товара) Побочный эффект: устанавливает hasItems = true на подкатегории. Подкатегория больше не может принимать дочерние подкатегории. ``` ### Обновить товар ``` PATCH /api/items/:itemId Тело запроса: (любое подмножество полей) { "name": "Новое название", "price": 899, "quantity": 80, "visible": false, "translations": { "ru": { "name": "Новое название" } } } Ответ 200: (обновлённый объект товара) ``` **Обновление изображений — всегда передавай полный массив:** ``` PATCH /api/items/:itemId Тело запроса: { "imgs": ["https://new1.jpg", "https://new2.jpg"] } Ответ 200: (обновлённый объект товара) ``` ### Удалить товар ``` DELETE /api/items/:itemId Ответ 204 No Content Побочный эффект: если в подкатегории не осталось товаров — hasItems становится false. Подкатегория снова может принимать дочерние подкатегории. ``` ### Массовое обновление товаров ``` PATCH /api/items/bulk Тело запроса: { "itemIds": ["item1", "item2", "item3"], "data": { "visible": true } } Ответ 204 No Content ``` --- ## Загрузка файлов ### Загрузить изображение ``` POST /api/upload Тело запроса: multipart/form-data image: File Ответ 201: { "url": "https://cdn.example.com/uploads/abc123.jpg" } ``` --- ## Мультиязычность (i18n) Поля `name`, `simpleDescription` и `description` поддерживают переводы через объект `translations`. ### Структура объекта переводов ```json { "translations": { "ru": { "name": "Название на русском", "simpleDescription": "Описание на русском", "description": [ { "key": "Ключ", "value": "Значение" } ] } } } ``` > Основные поля (`name`, `simpleDescription`, `description`) хранятся на **английском** (по умолчанию). > Переводы хранятся в `translations[langCode].*`. ### Запрос конкретного языка Передай параметр `?lang=ru` в GET-запросах. Бэкенд должен: 1. Вернуть `translations.ru.*` там, где перевод заполнен. 2. Откатиться к основному полю (английскому), если перевод отсутствует. ``` GET /api/items/:itemId?lang=ru GET /api/subcategories/:subcategoryId/items?lang=ru&page=1 ``` Альтернативно — заголовок `Accept-Language: ru`. ### Поддерживаемые языки | Код | Язык | |-----|----------| | en | Английский (по умолчанию) | | ru | Русский | --- ## Примечания - Все ответы — JSON. - Используй `PATCH` для частичного обновления — передавай только изменяемые поля. - `priority`: меньшее число — выше в списке. - Поддерживаемые значения `currency`: `USD`, `EUR`, `RUB`, `GBP`, `UAH`. - `badges`: необязательный массив строк. Стандартные значения с цветами в интерфейсе: `new`, `sale`, `exclusive`, `hot`, `limited`, `bestseller`, `featured`. Свои строки тоже допустимы. - `imgs`: при обновлении всегда передавай **полный** массив, не отдельные изображения. - `description`: массив пар `{ key, value }` — свободные атрибуты товара. - Автосохранение из бэкофиса отправляет `PATCH` с одним полем каждые ~500 мс. --- ## Бизнес-правила ### Вложенные подкатегории Иерархия выглядит так: ``` Категория (напр. Электроника) Подкатегория L1 (напр. Кухня) <- можно добавлять детей ИЛИ товары, не оба Подкатегория L2 (напр. Большая кухня) <- то же правило Подкатегория L3 (напр. Духовки) <- если есть товары — это листовой узел Товары... ``` Правила: - Категория всегда может принимать новые подкатегории (категории никогда не хранят товары напрямую). - Подкатегория с товарами (`hasItems: true`) **не может** принимать дочерние подкатегории. - `POST /api/subcategories/:id/subcategories` на узел с `hasItems: true` → `400 Bad Request`. - Подкатегория с дочерними подкатегориями не может принимать товары (товары только в листовых узлах). - При создании **первого товара** в подкатегории → `hasItems` становится `true`. - При удалении **последнего товара** → `hasItems` становится `false`; дочерние подкатегории снова можно добавлять. ### Структура URL (фронтенд маркетплейса) Поля `id` подкатегорий и товаров используются напрямую в URL маркетплейса: ``` /{categoryId}/{sub1Id}/{sub2Id}/.../{itemId} Примеры: /electronics/smartphones/iphone-15 /electronics/smartphones/apple/iphone-15-pro /furniture/living-room/sofas/corner-sofa-modelo ``` Поле `id` подкатегорий редактируется через `PATCH` для переименования слагов. ### Комментарии - `stars` — опционально, целое число от 1 до 5. - `author` — опционально (анонимные комментарии допустимы). - `createdAt` — строка в формате ISO 8601. ### Каскадное удаление | Удаляемый объект | Также удаляется | |---|---| | Категория | все подкатегории (рекурсивно) и их товары | | Подкатегория | все вложенные подкатегории (рекурсивно) и их товары | | Товар | ничего больше |