| name | add-api |
| description | Scaffolds a new API endpoint with router, handler, OpenAPI docs, and TS bindings. Use when adding REST endpoints to trader-api. |
| disable-model-invocation | true |
| user-invocable | true |
| argument-hint | <리소스명> [GET|POST|PUT|PATCH|DELETE] |
| allowed-tools | Read, Grep, Edit, Write, Bash(cargo *) |
| context | fork |
| agent | rust-impl |
API 엔드포인트 추가 워크플로우
새 엔드포인트를 $ARGUMENTS 사양으로 추가합니다.
0단계: 기존 구조 파악
ls crates/trader-api/src/routes/
grep -n "Router" crates/trader-api/src/routes/mod.rs
1단계: 핸들러 구현
위치: crates/trader-api/src/routes/<리소스>.rs (신규 또는 기존 파일)
필수 패턴
use axum::{extract::*, Json};
use crate::error::ApiErrorResponse;
#[utoipa::path(
get,
path = "/api/v1/<리소스>/{id}",
params(("id" = String, Path, description = "리소스 ID")),
responses(
(status = 200, description = "성공", body = MyResponse),
(status = 404, description = "리소스 없음")
),
tag = "<리소스>"
)]
pub async fn get_resource(
State(state): State<AppState>,
Path(id): Path<String>,
) -> Result<Json<MyResponse>, ApiErrorResponse> {
let data = MyRepository::find_by_id(&state.pool, &id)
.await
.map_err(|e| ApiErrorResponse::internal(e.to_string()))?
.ok_or_else(|| ApiErrorResponse::not_found("리소스를 찾을 수 없습니다"))?;
Ok(Json(data))
}
체크포인트
2단계: 응답 타입 정의
#[derive(Serialize, Deserialize, TS)]
#[ts(export)]
pub struct MyResponse {
pub id: String,
}
3단계: 라우터 등록
위치: crates/trader-api/src/routes/mod.rs
pub fn create_router(state: AppState) -> Router {
Router::new()
.route("/api/v1/<리소스>", get(get_resources))
.route("/api/v1/<리소스>/:id", get(get_resource))
.with_state(state)
}
4단계: 검증
# 1. 컴파일 확인
cargo check -p trader-api
# 2. Clippy
cargo clippy -p trader-api -- -D warnings
# 3. 타입 바인딩 생성 (TS 타입이 있는 경우)
cargo test --features ts-binding
# 4. OpenAPI 스펙 확인
# API 실행 후 http://localhost:3000/api-docs 에서 확인
검증 실패 시
- 에러 메시지에서 파일/라인 확인
- 해당 파일 수정
- 검증 명령 재실행 — 통과할 때까지 반복