# Add API

> Scaffolds a new API endpoint with router, handler, OpenAPI docs, and TS bindings. Use when adding REST endpoints to trader-api. Use when this capability is needed.

- Skill: `tomevault-io/add-api` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/add-api`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/add-api/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/add-api

---


# API 엔드포인트 추가 워크플로우

새 엔드포인트를 `$ARGUMENTS` 사양으로 추가합니다.

---

## 0단계: 기존 구조 파악

```bash
# 기존 라우트 모듈 확인
ls crates/trader-api/src/routes/

# 라우터 등록 위치 확인
grep -n "Router" crates/trader-api/src/routes/mod.rs
```

---

## 1단계: 핸들러 구현

**위치**: `crates/trader-api/src/routes/<리소스>.rs` (신규 또는 기존 파일)

### 필수 패턴

```rust
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> {
    // Repository 패턴 사용
    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))
}
```

### 체크포인트
- [ ] `#[utoipa::path]` 어노테이션 (OpenAPI 문서)
- [ ] `Result<_, ApiErrorResponse>` 반환
- [ ] `validator::Validate` 적용 (입력에)
- [ ] Repository 패턴 사용 (직접 SQL 금지)
- [ ] 금액 관련 필드는 `Decimal` 사용

---

## 2단계: 응답 타입 정의

```rust
#[derive(Serialize, Deserialize, TS)]
#[ts(export)]
pub struct MyResponse {
    pub id: String,
    // ...
}
```

- [ ] `#[derive(TS)]` + `#[ts(export)]` (프론트엔드 타입 자동 생성)

---

## 3단계: 라우터 등록

**위치**: `crates/trader-api/src/routes/mod.rs`

```rust
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단계: 검증

```powershell
# 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 에서 확인
```

### 검증 실패 시
1. 에러 메시지에서 파일/라인 확인
2. 해당 파일 수정
3. 검증 명령 재실행 — 통과할 때까지 반복

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/berrzebb) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-11 -->

