Agent Skillsmx-space/core › api-conventions

api-conventions

GitHub

定义 MX Space API 设计规范,涵盖控制器装饰器、认证、响应转换、分页及参数校验等标准。指导开发者编写符合规范的 HTTP 接口与控制器代码。

.claude/skills/api-conventions/SKILL.md mx-space/core

Trigger Scenarios

编写 API 控制器或端点 处理 HTTP 请求逻辑 设计 RESTful 接口规范

Install

npx skills add mx-space/core --skill api-conventions -g -y
More Options

Non-standard path

npx skills add https://github.com/mx-space/core/tree/master/.claude/skills/api-conventions -g -y

Use without installing

npx skills use mx-space/core@api-conventions

指定 Agent (Claude Code)

npx skills add mx-space/core --skill api-conventions -a claude-code -g -y

安装 repo 全部 skill

npx skills add mx-space/core --all -g -y

预览 repo 内 skill

npx skills add mx-space/core --list

SKILL.md

Frontmatter
{
    "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

// 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 (global APP_INTERCEPTOR) wraps every controller return value:

Return value Emitted
bare value T { data: T }
withMeta(data, meta) { data, meta }
undefined 204 No Content
@HTTPDecorators.RawResponse untouched — skips envelope and case conversion

withMeta (from ~/common/response/envelope.types) is detected by an internal Symbol, not by the presence of a data key — returning an object literal whose top-level keys include data gets double-wrapped. CI enforces this via scripts/check-controller-response-envelope.ts.

transformResponseCase (~/common/response/case-transform.ts) converts the response data/meta to snake_case at the wire boundary:

  • createdAtcreated_at
  • categoryIdcategory_id

Opt a field subtree out with @BypassCaseTransform(['items[].rawPayload']).

Pagination

Pagination belongs in meta, never merged into data. Build it with MetaObjectBuilder:

@Get('/')
async list(@Query() query: PagerDto) {
  const result = await this.postRepository.list({
    page: query.page,
    size: query.size,
    sortBy: query.sortBy,
    sortOrder: query.sortOrder,
  })

  const metaBuilder = new MetaObjectBuilder().view('card').pagination({
    page: result.pagination.currentPage,
    size: result.pagination.size,
    total: result.pagination.total,
    totalPages: result.pagination.totalPage,
  })

  return withMeta(result.data, metaBuilder.build())
}

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

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'

// 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() {}

Version History

  • c2ffb56 Current 2026-08-19 23:54

    更新响应转换机制以匹配信封/元数据响应模式,精简 AI 引导提示词,并强化翻译系统提示词。

  • a28bdf5 2026-07-25 05:37

Same Skill Collection

.claude/skills/create-e2e-test/SKILL.md
.claude/skills/create-module/SKILL.md
.claude/skills/mx-core-local-auth/SKILL.md
.claude/skills/mx-pg-controller-migration/SKILL.md
.claude/skills/mx-review/SKILL.md
.claude/skills/mxs-cli-ai-author/SKILL.md
.claude/skills/release-core/SKILL.md
.claude/skills/run-test/SKILL.md
.claude/skills/zod-patterns/SKILL.md
.claude/skills/mx-migration-author/SKILL.md

Metadata

Files
0
Version
c2ffb56
Hash
91bc35b9
Indexed
2026-07-25 05:37

Главная - Вики-сайт
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-20 08:56
浙ICP备14020137号-1 $Гость$