| name | api-conventions |
| description | MX Space API design conventions. Apply when writing controllers, API endpoints, or handling HTTP requests. |
| user-invocable | false |
MX Space API Design Conventions
Controller Decorators
@ApiController('posts')
@Controller('posts')
Authentication
@Auth()
async create() {}
async get(@IsAuthenticated() isAuth: boolean) {}
async get(@CurrentUser() user: UserModel) {}
Response Transformation
ResponseInterceptor automatically handles response format:
| Return Type | Transformed Result |
|---|
Array | { data: [...] } |
Object | Returned directly |
undefined | 204 No Content |
@Bypass | Returned as-is, skips transformation |
JSONTransformInterceptor converts all fields to snake_case:
createdAt → created_at
categoryId → category_id
Pagination
@Get('/')
async list(@Query() query: PagerDto) {
return this.postRepository.list({
page: query.page,
size: query.size,
sortBy: query.sortBy,
sortOrder: query.sortOrder,
})
}
For CRUD boilerplate, use BasePgCrudFactory:
@ApiController(paths)
export class LinkControllerCrud extends BasePgCrudFactory({
repository: LinkRepository,
}) {
@Get('/')
async gets(@Query() pager: PagerDto) {
const { size = 10, page = 1 } = pager
return this.repository.list(page, size)
}
}
Parameter Validation
@Get('/:id')
async get(@Param() params: EntityIdDto) {
return this.service.findById(params.id)
}
@Get('/:id')
async get(@Param() params: IntIdOrEntityIdDto) {}
@Get('/')
async list(@Query() query: PagerDto) {}
@Post('/')
async create(@Body() body: CreateDto) {}
HTTP Methods
| Method | Purpose | Status Code |
|---|
| GET | Retrieve resource | 200 |
| POST | Create resource | 201 |
| PUT | Full update | 200 |
| PATCH | Partial update | 200 |
| DELETE | Delete resource | 204 |
Error Handling
import { BusinessException } from '~/common/exceptions/biz.exception'
import { ErrorCodeEnum } from '~/constants/error-code.constant'
throw new BusinessException(ErrorCodeEnum.PostNotFound)
throw new BusinessException(ErrorCodeEnum.SlugNotAvailable, slug)
throw new BadRequestException('Invalid input')
throw new NotFoundException('Resource not found')
throw new UnauthorizedException('Not logged in')
Idempotency
@Post('/')
@HTTPDecorators.Idempotence()
async create() {}
@HTTPDecorators.Idempotence({ key: 'custom-key' })
Caching
@Get('/')
@HttpCache.disable
async list() {}
@HttpCache({ ttl: 60, key: 'my-key' })
async get() {}