updated api docs

This commit is contained in:
sdarbinyan
2026-02-20 00:59:46 +04:00
parent 0f3d0ae3ef
commit cb1349a5fd

264
API.md
View File

@@ -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 ## Projects
@@ -8,7 +11,7 @@ Simple endpoint reference for the Marketplace Backoffice.
``` ```
GET /api/projects GET /api/projects
Response: Response 200:
[ [
{ {
"id": "dexar", "id": "dexar",
@@ -20,13 +23,15 @@ Response:
] ]
``` ```
---
## Categories ## Categories
### Get Categories for Project ### Get Categories for Project
``` ```
GET /api/projects/:projectId/categories GET /api/projects/:projectId/categories
Response: Response 200:
[ [
{ {
"id": "cat1", "id": "cat1",
@@ -35,7 +40,7 @@ Response:
"priority": 1, "priority": 1,
"img": "https://...", "img": "https://...",
"projectId": "dexar", "projectId": "dexar",
"subcategories": [...] "subcategories": [ ...Subcategory[] ]
} }
] ]
``` ```
@@ -44,7 +49,7 @@ Response:
``` ```
GET /api/categories/:categoryId GET /api/categories/:categoryId
Response: Response 200:
{ {
"id": "cat1", "id": "cat1",
"name": "Electronics", "name": "Electronics",
@@ -52,7 +57,7 @@ Response:
"priority": 1, "priority": 1,
"img": "https://...", "img": "https://...",
"projectId": "dexar", "projectId": "dexar",
"subcategories": [...] "subcategories": [ ...Subcategory[] ]
} }
``` ```
@@ -62,123 +67,157 @@ POST /api/projects/:projectId/categories
Body: Body:
{ {
"name": "New Category", "name": "New Category", // required
"visible": true, "visible": true,
"priority": 10, "priority": 10,
"img": "https://..." "img": "https://..."
} }
Response: (created category object) Response 201: (created category object)
``` ```
### Update Category ### Update Category
``` ```
PATCH /api/categories/:categoryId PATCH /api/categories/:categoryId
Body: (any field to update) Body: (any subset of fields)
{ {
"name": "Updated Name", "name": "Updated Name",
"visible": false, "visible": false,
"priority": 5 "priority": 5
} }
Response: (updated category object) Response 200: (updated category object)
``` ```
### Delete Category ### Delete Category
``` ```
DELETE /api/categories/:categoryId DELETE /api/categories/:categoryId
Response: 204 No Content Response 204 No Content
Note: Cascades and deletes all subcategories and items under this category
``` ```
---
## Subcategories ## 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 GET /api/categories/:categoryId/subcategories
Response: Response 200: Subcategory[] (nested subcategories populated recursively)
[
{
"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
``` ```
### Get Single Subcategory ### Get Single Subcategory
``` ```
GET /api/subcategories/:subcategoryId 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 POST /api/categories/:categoryId/subcategories
Note: categoryId can be either a category ID or a parent subcategory ID for nested structure
Body: Body:
{ {
"id": "custom-id", // Optional, auto-generated if not provided "id": "custom-id", // optional, auto-generated if omitted (used in URL routing)
"name": "New Subcategory", "name": "Smartphones", // required
"visible": true, "visible": true,
"priority": 10 "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 ### Update Subcategory
``` ```
PATCH /api/subcategories/:subcategoryId 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", "name": "Updated Name",
"visible": false "visible": false,
"priority": 3
} }
Response: (updated subcategory object) Response 200: (updated subcategory object)
``` ```
### Delete Subcategory ### Delete Subcategory
``` ```
DELETE /api/subcategories/:subcategoryId 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
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 Items (Paginated)
``` ```
GET /api/subcategories/:subcategoryId/items?page=1&limit=20&search=phone&visible=true GET /api/subcategories/:subcategoryId/items
Query Params: Query Params:
- page: number (default: 1) page number (default: 1)
- limit: number (default: 20) limit number (default: 20)
- search: string (optional - filters by name) search string optional - filters by name (case-insensitive)
- visible: boolean (optional - filters by visibility) visible boolean optional - filter by visibility
- tags: string (optional - comma separated tags) tags string optional - comma-separated tag list
Response: Response 200:
{ {
"items": [ "items": [
{ {
@@ -219,7 +258,7 @@ Response:
``` ```
GET /api/items/:itemId GET /api/items/:itemId
Response: (item object with all fields) Response 200: (full item object)
``` ```
### Create Item ### Create Item
@@ -228,28 +267,31 @@ POST /api/subcategories/:subcategoryId/items
Body: Body:
{ {
"name": "New Product", "name": "New Product", // required
"visible": true, "visible": true,
"priority": 10, "priority": 10,
"quantity": 100, "quantity": 100,
"price": 999, "price": 999,
"currency": "USD", "currency": "USD", // USD | EUR | RUB | GBP | UAH
"imgs": ["https://..."], "imgs": ["https://..."],
"tags": ["new"], "tags": ["new"],
"simpleDescription": "Product description", "simpleDescription": "Short description",
"description": [ "description": [
{ "key": "Size", "value": "Large" } { "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 ### Update Item
``` ```
PATCH /api/items/:itemId PATCH /api/items/:itemId
Body: (any field to update) Body: (any subset of fields)
{ {
"name": "Updated Name", "name": "Updated Name",
"price": 899, "price": 899,
@@ -257,27 +299,29 @@ Body: (any field to update)
"visible": false "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: 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 200: (updated item object)
Response: (updated item object)
``` ```
### Delete Item ### Delete Item
``` ```
DELETE /api/items/:itemId 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 ### Bulk Update Items
@@ -292,9 +336,11 @@ Body:
} }
} }
Response: 204 No Content Response 204 No Content
``` ```
---
## Upload ## Upload
### Upload Image ### Upload Image
@@ -302,41 +348,75 @@ Response: 204 No Content
POST /api/upload POST /api/upload
Body: multipart/form-data 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 ## Notes
- All endpoints return JSON - All responses are JSON.
- Use PATCH for partial updates - Use `PATCH` for partial updates - send only the fields you want to change.
- Priority: lower numbers = appears first - `priority`: lower number = appears first in the list.
- Currency codes: USD, EUR, RUB, GBP, UAH - `currency` supported values: `USD`, `EUR`, `RUB`, `GBP`, `UAH`.
- Images: array of URLs - `imgs`: always send the **complete** array on update, not individual images.
- Description: array of key-value pairs - `description`: array of `{ key, value }` pairs - free-form attributes per item.
- Auto-save triggers PATCH with single field every 500ms - Auto-save from the backoffice fires `PATCH` with a single field every ~500 ms.
### Business Rules
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** ## Business Rules
- 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
3. **Comments** ### Nested Subcategories
- Stars field is optional (1-5 rating)
- createdAt is ISO 8601 timestamp
- author is optional (can be anonymous)
4. **URL Structure for Marketplace** The hierarchy works like this:
- Items: `/{categoryId}/{subcategoryId}/.../{itemId}`
- Example: `/electronics/smartphones/iphone-15` ```
- Example nested: `/electronics/smartphones/apple/iphone-15` 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 |