updated api docs

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

248
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
@@ -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
```
GET /api/categories/:categoryId/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).
Response:
[
### Subcategory Object
```json
{
"id": "sub1",
"name": "Smartphones",
"visible": true,
"priority": 1,
"img": "https://...",
"categoryId": "cat1",
"categoryId": "cat1", // always the ROOT category ID
"parentId": "cat1", // direct parent ID (category OR subcategory)
"itemCount": 15,
"hasItems": true,
"subcategories": []
"subcategories": [] // nested children (empty when hasItems = true)
}
]
```
Note:
- Subcategories can have nested subcategories (recursive)
- If hasItems is true, cannot create child subcategories
> `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 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`
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 |