| 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 ์์ ํ์ธ
๊ฒ์ฆ ์คํจ ์
- ์๋ฌ ๋ฉ์์ง์์ ํ์ผ/๋ผ์ธ ํ์ธ
- ํด๋น ํ์ผ ์์
- ๊ฒ์ฆ ๋ช
๋ น ์ฌ์คํ โ ํต๊ณผํ ๋๊น์ง ๋ฐ๋ณต