5.8 KiB
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)
{
"statusCode": 200,
"success": true,
"data": {
"id": "01HXYZ...",
"phone": "09362532122"
}
}
Success (paginated list)
Services return PaginatedResult<T>; the interceptor wraps it:
{
"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
{
"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
@ApiPropertyfor examples and descriptions.
Controller pattern
@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
timestamptzin UTC. - Serialized as ISO 8601 strings in JSON responses.
- Token
expirefields 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
- Create or extend a DTO with validation and Swagger decorators.
- Add a thin controller method with correct
public/oradmin/path. - Apply the appropriate guard and
@Permissionsif admin-only. - Implement logic in the service; use repository for queries.
- Return typed data — the response interceptor handles wrapping.
- Document with
@ApiOperationand related Swagger decorators. - Add migration if new entities or columns are required.