Files
negareh-api/docs/api-conventions.md
T
2026-07-02 15:59:46 +03:30

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 @ApiProperty for 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 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.