Skill Index

core/

api-conventions

community[skill]

MX Space API design conventions. Apply when writing controllers, API endpoints, or handling HTTP requests.

$/plugin install core

details

MX Space API Design Conventions

Controller Decorators

// Use @ApiController instead of @Controller
// Dev environment has no prefix, production auto-adds /api/v{version} prefix
@ApiController('posts')  // ✓
@Controller('posts')     // ✗

Authentication

// Endpoints requiring login
@Auth()
async create() {}

// Optional auth (get current user status)
async get(@IsAuthenticated() isAuth: boolean) {}

// Get current user
async get(@CurrentUser() user: UserModel) {}

Response Transformation

ResponseInterceptor automatically handles response format:

Return TypeTransformed Result
Array{ data: [...] }
ObjectReturned directly
undefined204 No Content
@BypassReturned as-is, skips transformation

JSONTransformInterceptor converts all fields to snake_case:

  • createdAtcreated_at
  • categoryIdcategory_id

Pagination

// Controller: return PaginationResult<T> from the repository
@Get('/')
async list(@Query() query: PagerDto) {
  return this.postRepository.list({
    page: query.page,
    size: query.size,
    sortBy: query.sortBy,
    sortOrder: query.sortOrder,
  })
}

// Repository returns { data: T[], pagination: {...} } directly
// The ResponseInterceptor auto-wraps this as { data: [...], pagination: {...} }

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

// Path parameters — use EntityIdDto for Snowflake entity IDs
@Get('/:id')
async get(@Param() params: EntityIdDto) {
  return this.service.findById(params.id)
}

// For integer IDs or entity IDs (e.g. notes with nid)
@Get('/:id')
async get(@Param() params: IntIdOrEntityIdDto) {}

// Query parameters
@Get('/')
async list(@Query() query: PagerDto) {}

// Request body
@Post('/')
async create(@Body() body: CreateDto) {}

HTTP Methods

MethodPurposeStatus Code
GETRetrieve resource200
POSTCreate resource201
PUTFull update200
PATCHPartial update200
DELETEDelete resource204

Error Handling

import { BusinessException } from '~/common/exceptions/biz.exception'
import { ErrorCodeEnum } from '~/constants/error-code.constant'

// Business errors
throw new BusinessException(ErrorCodeEnum.PostNotFound)
throw new BusinessException(ErrorCodeEnum.SlugNotAvailable, slug)

// HTTP errors
throw new BadRequestException('Invalid input')
throw new NotFoundException('Resource not found')
throw new UnauthorizedException('Not logged in')

Idempotency

// Add idempotency protection for create operations
@Post('/')
@HTTPDecorators.Idempotence()
async create() {}

// Custom idempotency key
@HTTPDecorators.Idempotence({ key: 'custom-key' })

Caching

// Disable cache
@Get('/')
@HttpCache.disable
async list() {}

// Custom cache
@HttpCache({ ttl: 60, key: 'my-key' })
async get() {}

technical

github
mx-space/core
stars
530
license
NOASSERTION
contributors
33
last commit
2026-05-29T06:39:36Z
file
.claude/skills/api-conventions/SKILL.md

related