220 lines
5.8 KiB
Markdown
220 lines
5.8 KiB
Markdown
# API Conventions
|
|
|
|
HTTP API design standards for Negareh backend. Swagger documentation is available at `/docs` when the server is running.
|
|
|
|
## Base URL
|
|
|
|
No global path prefix. Routes encode the audience and resource directly:
|
|
|
|
```
|
|
POST /public/auth/otp/request
|
|
GET /admin/orders
|
|
GET /public/orders/:id
|
|
```
|
|
|
|
## Route naming
|
|
|
|
### Audience prefixes
|
|
|
|
| Prefix | Description |
|
|
|--------|-------------|
|
|
| `public/` | Customer-facing endpoints |
|
|
| `admin/` | Back-office endpoints |
|
|
|
|
Auth endpoints embed the audience in the path: `public/auth/...`, `admin/auth/...`.
|
|
|
|
### HTTP methods
|
|
|
|
| Method | Usage |
|
|
|--------|-------|
|
|
| `GET` | Read single resource or list |
|
|
| `POST` | Create resource or action (e.g. OTP request) |
|
|
| `PATCH` | Partial update |
|
|
| `PUT` | Full replace (rare) |
|
|
| `DELETE` | Soft or hard delete |
|
|
|
|
### Path style
|
|
|
|
- Use **plural nouns** for collections: `/admin/orders`, `/public/tickets`.
|
|
- Use **path parameters** for IDs: `/admin/orders/:id`.
|
|
- Use **query parameters** for filtering, sorting, and pagination.
|
|
- Nest related actions under the resource: `/admin/orders/:id/assign-designer`.
|
|
|
|
Controllers use `@Controller()` with the full path on each method (no shared controller prefix), except where a module uses a short prefix like `@Controller('admin')`.
|
|
|
|
## Request format
|
|
|
|
### Headers
|
|
|
|
| Header | Required | Description |
|
|
|--------|----------|-------------|
|
|
| `Content-Type: application/json` | Yes (JSON bodies) | Standard JSON requests |
|
|
| `Authorization: Bearer <token>` | Protected routes | JWT access token |
|
|
|
|
### Validation
|
|
|
|
All inputs are validated through DTO classes with `class-validator`. The global `ValidationPipe` has `transform: true`, so query strings are coerced to the correct types.
|
|
|
|
Invalid input returns `400` with validation messages in the error envelope.
|
|
|
|
### File uploads
|
|
|
|
Multipart uploads use `@fastify/multipart`. Upload endpoints live in the `uploader` module and return file metadata/URLs from S3.
|
|
|
|
## Response format
|
|
|
|
### Success (single resource)
|
|
|
|
```json
|
|
{
|
|
"statusCode": 200,
|
|
"success": true,
|
|
"data": {
|
|
"id": "01HXYZ...",
|
|
"phone": "09362532122"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Success (paginated list)
|
|
|
|
Services return `PaginatedResult<T>`; the interceptor wraps it:
|
|
|
|
```json
|
|
{
|
|
"statusCode": 200,
|
|
"success": true,
|
|
"data": [
|
|
{ "id": "01HXYZ...", "status": "pending" }
|
|
],
|
|
"meta": {
|
|
"total": 42,
|
|
"page": 1,
|
|
"limit": 10,
|
|
"totalPages": 5
|
|
}
|
|
}
|
|
```
|
|
|
|
### Pagination query parameters
|
|
|
|
Common fields on list DTOs:
|
|
|
|
| Param | Type | Default | Description |
|
|
|-------|------|---------|-------------|
|
|
| `page` | number | `1` | Page number (1-based) |
|
|
| `limit` | number | `10` | Items per page |
|
|
| `search` | string | — | Free-text search |
|
|
| `orderBy` | string | `createdAt` | Sort field |
|
|
| `order` | `asc` \| `desc` | `desc` | Sort direction |
|
|
|
|
Some endpoints may use cursor-based pagination and return `nextCursor` at the top level.
|
|
|
|
### Error
|
|
|
|
```json
|
|
{
|
|
"statusCode": 400,
|
|
"success": false,
|
|
"error": {
|
|
"message": ["Phone number is invalid"]
|
|
}
|
|
}
|
|
```
|
|
|
|
`message` is always an array of strings, even for a single error.
|
|
|
|
### HTTP status codes
|
|
|
|
| Code | When |
|
|
|------|------|
|
|
| `200` | Successful GET, PATCH, DELETE |
|
|
| `201` | Successful POST (create) — when explicitly set |
|
|
| `400` | Validation failure, bad input |
|
|
| `401` | Missing or invalid token |
|
|
| `403` | Authenticated but insufficient permissions |
|
|
| `404` | Resource not found |
|
|
| `429` | Rate limit exceeded |
|
|
| `500` | Unexpected server error |
|
|
|
|
## Swagger
|
|
|
|
- Title: **Negareh API**
|
|
- Path: `/docs`
|
|
- Bearer auth scheme enabled — use the Authorize button to set JWT.
|
|
- Controllers document endpoints with `@ApiTags`, `@ApiOperation`, `@ApiBody`, `@ApiBearerAuth`.
|
|
- DTO fields use `@ApiProperty` for examples and descriptions.
|
|
|
|
## Controller pattern
|
|
|
|
```typescript
|
|
@ApiTags('orders')
|
|
@ApiBearerAuth()
|
|
@Controller()
|
|
export class OrderController {
|
|
constructor(private readonly orderService: OrderService) {}
|
|
|
|
// User route
|
|
@Get('public/orders')
|
|
@UseGuards(AuthGuard)
|
|
@ApiOperation({ summary: 'Get all orders with pagination and filters' })
|
|
findAll(@Query() dto: FindOrdersDto, @UserId() userId: string) {
|
|
return this.orderService.findOrdersAsUser(userId, dto);
|
|
}
|
|
|
|
// Admin route
|
|
@Post('admin/orders')
|
|
@UseGuards(AdminAuthGuard)
|
|
@Permissions(PermissionEnum.CREATE_ORDER)
|
|
@ApiOperation({ summary: 'Create order' })
|
|
create(@AdminId() adminId: string, @Body() body: CreateOrderAsAdminDto) {
|
|
return this.orderService.createOrderAsAdmin(adminId, body);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Identifiers
|
|
|
|
- Resource IDs are **ULIDs** (26-character strings).
|
|
- Pass IDs as path or body fields; do not use integer IDs.
|
|
|
|
## Dates and times
|
|
|
|
- Stored as `timestamptz` in UTC.
|
|
- Serialized as ISO 8601 strings in JSON responses.
|
|
- Token `expire` fields use Unix milliseconds.
|
|
|
|
## CORS
|
|
|
|
CORS is enabled for all origins in development with credentials support:
|
|
|
|
- Methods: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `OPTIONS`
|
|
- `credentials: true`
|
|
|
|
Tighten `origin` in production deployments.
|
|
|
|
## Rate limiting
|
|
|
|
Global default: **5 requests per 60 seconds** per IP (`ThrottlerModule`).
|
|
|
|
Stricter limits on sensitive routes:
|
|
|
|
- OTP request: 3 per 180 seconds
|
|
- Refresh token: custom decorator (`@RefreshTokenRateLimit()`)
|
|
|
|
Exceeded limits return `429 Too Many Requests`.
|
|
|
|
## Versioning
|
|
|
|
The API is currently unversioned (`v1` is implicit). Breaking changes should be coordinated with all clients (admin console, mobile app) before deployment.
|
|
|
|
## Adding a new endpoint checklist
|
|
|
|
1. Create or extend a DTO with validation and Swagger decorators.
|
|
2. Add a thin controller method with correct `public/` or `admin/` path.
|
|
3. Apply the appropriate guard and `@Permissions` if admin-only.
|
|
4. Implement logic in the service; use repository for queries.
|
|
5. Return typed data — the response interceptor handles wrapping.
|
|
6. Document with `@ApiOperation` and related Swagger decorators.
|
|
7. Add migration if new entities or columns are required.
|