From cb1349a5fd0c9151d8340435444ea004016d7059 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Fri, 20 Feb 2026 00:59:46 +0400 Subject: [PATCH] updated api docs --- API.md | 264 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 172 insertions(+), 92 deletions(-) diff --git a/API.md b/API.md index e3690e4..744f122 100644 --- a/API.md +++ b/API.md @@ -1,6 +1,9 @@ -# API Documentation # +# API Documentation -Simple endpoint reference for the Marketplace Backoffice. +Endpoint reference for the Marketplace Backoffice. +Base URL: `https://your-api-domain.com/api` + +--- ## Projects @@ -8,7 +11,7 @@ Simple endpoint reference for the Marketplace Backoffice. ``` GET /api/projects -Response: +Response 200: [ { "id": "dexar", @@ -20,13 +23,15 @@ Response: ] ``` +--- + ## Categories ### Get Categories for Project ``` GET /api/projects/:projectId/categories -Response: +Response 200: [ { "id": "cat1", @@ -35,7 +40,7 @@ Response: "priority": 1, "img": "https://...", "projectId": "dexar", - "subcategories": [...] + "subcategories": [ ...Subcategory[] ] } ] ``` @@ -44,7 +49,7 @@ Response: ``` GET /api/categories/:categoryId -Response: +Response 200: { "id": "cat1", "name": "Electronics", @@ -52,7 +57,7 @@ Response: "priority": 1, "img": "https://...", "projectId": "dexar", - "subcategories": [...] + "subcategories": [ ...Subcategory[] ] } ``` @@ -62,123 +67,157 @@ POST /api/projects/:projectId/categories Body: { - "name": "New Category", + "name": "New Category", // required "visible": true, "priority": 10, "img": "https://..." } -Response: (created category object) +Response 201: (created category object) ``` ### Update Category ``` PATCH /api/categories/:categoryId -Body: (any field to update) +Body: (any subset of fields) { "name": "Updated Name", "visible": false, "priority": 5 } -Response: (updated category object) +Response 200: (updated category object) ``` ### Delete Category ``` DELETE /api/categories/:categoryId -Response: 204 No Content +Response 204 No Content + +Note: Cascades and deletes all subcategories and items under this category ``` +--- + ## Subcategories -### Get Subcategories +Subcategories are **recursive** - they can be nested under a root `Category` or under +another `Subcategory`. The nesting depth is unlimited, with one constraint: +**a subcategory that already contains items cannot have child subcategories** (and vice versa). + +### Subcategory Object +```json +{ + "id": "sub1", + "name": "Smartphones", + "visible": true, + "priority": 1, + "img": "https://...", + "categoryId": "cat1", // always the ROOT category ID + "parentId": "cat1", // direct parent ID (category OR subcategory) + "itemCount": 15, + "hasItems": true, + "subcategories": [] // nested children (empty when hasItems = true) +} +``` + +> `categoryId` is always the **root-level category** this subtree belongs to. +> `parentId` is the **direct parent** - it can be either a category ID or a subcategory ID. + +--- + +### Get Subcategories Under a Category ``` GET /api/categories/:categoryId/subcategories -Response: -[ - { - "id": "sub1", - "name": "Smartphones", - "visible": true, - "priority": 1, - "img": "https://...", - "categoryId": "cat1", - "itemCount": 15, - "hasItems": true, - "subcategories": [] - } -] - -Note: -- Subcategories can have nested subcategories (recursive) -- If hasItems is true, cannot create child subcategories +Response 200: Subcategory[] (nested subcategories populated recursively) ``` ### Get Single Subcategory ``` GET /api/subcategories/:subcategoryId -Response: (subcategory object with nested subcategories if any) +Response 200: Subcategory object (with nested subcategories if any) ``` -### Create Subcategory +### Create Subcategory Under a Category (level 1) ``` POST /api/categories/:categoryId/subcategories -Note: categoryId can be either a category ID or a parent subcategory ID for nested structure - Body: { - "id": "custom-id", // Optional, auto-generated if not provided - "name": "New Subcategory", + "id": "custom-id", // optional, auto-generated if omitted (used in URL routing) + "name": "Smartphones", // required "visible": true, "priority": 10 } -Response: (created subcategory object) +Response 201: (created subcategory object) +Error 400: if category does not exist +``` -Error: Returns 400 if parent subcategory already has items +### Create Subcategory Under a Parent Subcategory (level 2+, nested) +``` +POST /api/subcategories/:parentSubcategoryId/subcategories + +Body: +{ + "id": "custom-id", // optional, auto-generated if omitted (used in URL routing) + "name": "Apple", // required + "visible": true, + "priority": 10 +} + +Response 201: (created subcategory object) +Error 400: if parent subcategory has items (hasItems = true) +Error 404: if parent subcategory does not exist ``` ### Update Subcategory ``` PATCH /api/subcategories/:subcategoryId -Body: (any field) +Body: (any subset of fields) { - "id": "new-id", // ID is now editable (used for routing) + "id": "new-slug", // ID is editable - used for marketplace URL routing "name": "Updated Name", - "visible": false + "visible": false, + "priority": 3 } -Response: (updated subcategory object) +Response 200: (updated subcategory object) ``` ### Delete Subcategory ``` DELETE /api/subcategories/:subcategoryId -Response: 204 No Content +Response 204 No Content + +Note: Cascades and deletes all nested subcategories and items under this subcategory ``` +--- + ## Items +Items always belong to the **deepest subcategory** in the hierarchy (a leaf node). +A subcategory with at least one item has `hasItems: true` and cannot have child subcategories. + ### Get Items (Paginated) ``` -GET /api/subcategories/:subcategoryId/items?page=1&limit=20&search=phone&visible=true +GET /api/subcategories/:subcategoryId/items Query Params: -- page: number (default: 1) -- limit: number (default: 20) -- search: string (optional - filters by name) -- visible: boolean (optional - filters by visibility) -- tags: string (optional - comma separated tags) + page number (default: 1) + limit number (default: 20) + search string optional - filters by name (case-insensitive) + visible boolean optional - filter by visibility + tags string optional - comma-separated tag list -Response: +Response 200: { "items": [ { @@ -219,7 +258,7 @@ Response: ``` GET /api/items/:itemId -Response: (item object with all fields) +Response 200: (full item object) ``` ### Create Item @@ -228,28 +267,31 @@ POST /api/subcategories/:subcategoryId/items Body: { - "name": "New Product", + "name": "New Product", // required "visible": true, "priority": 10, "quantity": 100, "price": 999, - "currency": "USD", + "currency": "USD", // USD | EUR | RUB | GBP | UAH "imgs": ["https://..."], "tags": ["new"], - "simpleDescription": "Product description", + "simpleDescription": "Short description", "description": [ { "key": "Size", "value": "Large" } ] } -Response: (created item object) +Response 201: (created item object) + +Side effect: Sets hasItems = true on the subcategory. + The subcategory can no longer receive child subcategories. ``` ### Update Item ``` PATCH /api/items/:itemId -Body: (any field to update) +Body: (any subset of fields) { "name": "Updated Name", "price": 899, @@ -257,27 +299,29 @@ Body: (any field to update) "visible": false } -Response: (updated item object) +Response 200: (updated item object) ``` -**Example: Update only images** +**Updating images - always send the full array:** ``` -PATCH /api/items/item123 +PATCH /api/items/:itemId Body: { - "imgs": ["https://new-image1.jpg", "https://new-image2.jpg"] + "imgs": ["https://new1.jpg", "https://new2.jpg"] } -Note: Send the complete array of images (not just changed ones) -Response: (updated item object) +Response 200: (updated item object) ``` ### Delete Item ``` DELETE /api/items/:itemId -Response: 204 No Content +Response 204 No Content + +Side effect: If no items remain in the subcategory, sets hasItems = false. + The subcategory can then receive child subcategories again. ``` ### Bulk Update Items @@ -292,9 +336,11 @@ Body: } } -Response: 204 No Content +Response 204 No Content ``` +--- + ## Upload ### Upload Image @@ -302,41 +348,75 @@ Response: 204 No Content POST /api/upload Body: multipart/form-data -- image: File + image: File -Response: +Response 201: { - "url": "https://cdn.example.com/image.jpg" + "url": "https://cdn.example.com/uploads/abc123.jpg" } ``` +--- + ## Notes -- All endpoints return JSON -- Use PATCH for partial updates -- Priority: lower numbers = appears first -- Currency codes: USD, EUR, RUB, GBP, UAH -- Images: array of URLs -- Description: array of key-value pairs -- Auto-save triggers PATCH with single field every 500ms -### Business Rules +- All responses are JSON. +- Use `PATCH` for partial updates - send only the fields you want to change. +- `priority`: lower number = appears first in the list. +- `currency` supported values: `USD`, `EUR`, `RUB`, `GBP`, `UAH`. +- `imgs`: always send the **complete** array on update, not individual images. +- `description`: array of `{ key, value }` pairs - free-form attributes per item. +- Auto-save from the backoffice fires `PATCH` with a single field every ~500 ms. -1. **Nested Subcategories** - - Subcategories can have unlimited nesting levels - - A subcategory with items (`hasItems: true`) cannot have child subcategories - - Creating a subcategory under a parent with items will fail +--- -2. **Item Management** - - When first item is created in a subcategory, `hasItems` is set to true - - When last item is deleted, `hasItems` is set to false - - Items belong to the deepest subcategory in the hierarchy +## Business Rules -3. **Comments** - - Stars field is optional (1-5 rating) - - createdAt is ISO 8601 timestamp - - author is optional (can be anonymous) +### Nested Subcategories -4. **URL Structure for Marketplace** - - Items: `/{categoryId}/{subcategoryId}/.../{itemId}` - - Example: `/electronics/smartphones/iphone-15` - - Example nested: `/electronics/smartphones/apple/iphone-15` \ No newline at end of file +The hierarchy works like this: + +``` +Category (e.g. Electronics) + Subcategory L1 (e.g. Kitchen) <- can add more children OR items, not both + Subcategory L2 (e.g. Big Kitchen) <- same rule + Subcategory L3 (e.g. Ovens) <- if this has items, it is a leaf + Items... +``` + +Rules: +- A category can always receive new subcategories (categories never hold items directly). +- A subcategory that **has items** (`hasItems: true`) **cannot** receive child subcategories. + - `POST /api/subcategories/:id/subcategories` on a node with `hasItems: true` -> `400 Bad Request`. +- A subcategory that **has children** cannot have items added to it (items only go to leaf nodes). +- When the **first item** is created in a subcategory -> `hasItems` becomes `true`. +- When the **last item** is deleted -> `hasItems` becomes `false`; child subcategories can be added again. + +### URL Structure (Marketplace Frontend) + +Subcategory and item `id` fields are used directly in the marketplace URL path: + +``` +/{categoryId}/{sub1Id}/{sub2Id}/.../{itemId} + +Examples: + /electronics/smartphones/iphone-15 + /electronics/smartphones/apple/iphone-15-pro + /furniture/living-room/sofas/corner-sofa-modelo +``` + +The `id` field on subcategories is editable via `PATCH` to allow renaming slugs. + +### Comments + +- `stars` is optional, integer 1-5. +- `author` is optional (anonymous comments allowed). +- `createdAt` is ISO 8601 string. + +### Cascade Deletes + +| Delete target | Also deletes | +|---|---| +| Category | all subcategories (recursive) and their items | +| Subcategory | all nested subcategories (recursive) and their items | +| Item | nothing else |