API 클라이언트
Framedash API 클라이언트(@framedash/api-client)는 Framedash 개발자 플랫폼 REST API를 타입과 함께 다루는 TypeScript / JavaScript 클라이언트입니다. @framedash/cli와 @framedash/mcp-server 내부에서 사용하는 것과 동일한 클라이언트를 독립 패키지로 공개하여, Framedash 텔레메트리를 사내 도구, 대시보드, 자동화에 직접 통합할 수 있습니다.
인증, 프로젝트 범위 경로 생성, 응답 언래핑, 구조화된 오류 처리에 더해 몇 가지 보안 가드(HTTPS 전용 베이스 URL, 자격 증명을 유출하지 않는 리다이렉트 거부, 요청 타임아웃)를 내장하고 있어 데이터에 집중할 수 있습니다.
요구 사항
섹션 제목: “요구 사항”- Node.js 18 이상(전역
fetch,AbortSignal.timeout,node:net사용) - Web API 접근용 관리자 API 키(
fd_admin_접두사)
npm install @framedash/api-client빠른 시작
섹션 제목: “빠른 시작”import { ApiClient, ApiError } from "@framedash/api-client";
const client = new ApiClient({ baseUrl: "https://app.framedash.dev", apiKey: process.env.FRAMEDASH_API_KEY ?? "", projectId: process.env.FRAMEDASH_PROJECT_ID ?? "", onError: (err: ApiError) => { // onError는 성공이 아닌 모든 응답에서 호출됩니다. 반드시 throw(또는 종료)해야 합니다. // 반환 타입은 `never`입니다. throw된 값은 await로 전파됩니다. throw err; },});
// 프로젝트 범위 GET -> /api/v1/projects/{projectId}/dashboard?days=30const dashboard = await client.get(client.projectPath("dashboard?days=30"));console.log(dashboard);클라이언트는 API 엔벨로프를 자동으로 언래핑합니다. 성공 시 { "success": true, "data": ... } 응답은 data 값으로만 해석됩니다.
ApiClient 생성자는 단일 옵션 객체를 받습니다:
| 옵션 | 타입 | 설명 |
|---|---|---|
baseUrl | string | 애플리케이션 호스트 URL(예: https://app.framedash.dev). HTTPS 필수(HTTP는 localhost / 루프백만 허용). |
apiKey | string | 관리자 API 키. 모든 요청의 X-API-Key 헤더로 전송됩니다. |
projectId | string | 기본 프로젝트 UUID. projectPath()에 필수. 프로젝트 외 경로에서는 X-Project-Id 헤더로도 전송됩니다. |
onError | (error: ApiError) => never | 성공이 아닌 응답에서 호출됩니다. throw 또는 프로세스 종료가 필수입니다. |
베이스 URL은 클라이언트 생성 시 검증되며, 안전하지 않거나 잘못된 URL은 즉시 throw됩니다. 동일한 검사는 공개 함수 assertSafeBaseUrl(baseUrl)로 직접 실행할 수도 있습니다.
요청 보내기
섹션 제목: “요청 보내기”4개의 HTTP 헬퍼가 Web API를 다룹니다. 각각 언래핑된 data 페이로드를 반환하며, 제네릭 파라미터로 타입을 지정할 수 있습니다:
const data = await client.get<MyType>("/api/v1/...");await client.post("/api/v1/...", body);await client.patch("/api/v1/...", body);await client.delete("/api/v1/...");프로젝트 범위 경로
섹션 제목: “프로젝트 범위 경로”대부분의 엔드포인트는 프로젝트 단위입니다. projectPath(suffix)는 클라이언트에 설정된 projectId로 /api/v1/projects/{projectId}/{suffix}를 구성합니다:
// GET /api/v1/projects/{projectId}/statusconst status = await client.get(client.projectPath("status"));
// GET /api/v1/projects/{projectId}/retention?days=30const retention = await client.get(client.projectPath("retention?days=30"));
// GET /api/v1/projects/{projectId}/mapsconst maps = await client.get(client.projectPath("maps"));projectId가 설정되지 않은 상태로 projectPath()를 호출하면 throw됩니다.
프로젝트 컨텍스트 전환
섹션 제목: “프로젝트 컨텍스트 전환”withProject(projectId)는 동일한 베이스 URL, API 키, 오류 핸들러를 재사용하면서 다른 프로젝트에 바인딩된 새 클라이언트를 반환합니다. 원본 클라이언트는 변경되지 않습니다:
const other = client.withProject("another-project-uuid");const otherStatus = await other.get(other.projectPath("status"));
// 현재 바인딩된 프로젝트 ID:console.log(client.currentProjectId);SQL 쿼리 실행
섹션 제목: “SQL 쿼리 실행”const rows = await client.post("/api/v1/query", { sql: "SELECT event_name, count() FROM events GROUP BY event_name", project_id: client.currentProjectId, limit: 100,});알림 관리
섹션 제목: “알림 관리”// 알림 규칙 목록 조회const alerts = await client.get(client.projectPath("alerts"));
// 알림 규칙 생성const created = await client.post(client.projectPath("alerts"), { name: "FPS Alert", // ...나머지 규칙 필드});
// 알림 규칙 비활성화await client.delete(client.projectPath(`alerts/${alertId}`));콘텐츠 레지스트리 가져오기
섹션 제목: “콘텐츠 레지스트리 가져오기”// content 엔드포인트는 경로상 프로젝트 범위가 아닙니다.// 클라이언트가 설정된 projectId에서 X-Project-Id 헤더를 자동으로 추가합니다.// 일괄 upsert 본문은 배열을 entries 속성으로 감쌉니다(요청당 최대 500개).await client.post("/api/v1/content", { entries });const content = await client.get("/api/v1/content");엔드포인트, 쿼리 파라미터, 페이로드 구조의 전체 목록은 API 개요를 참조하세요.
오류 처리
섹션 제목: “오류 처리”성공이 아닌 모든 응답(네트워크 오류, 2xx 이외의 상태, success: true가 없는 본문)은 ApiError로 변환되어 onError 콜백에 전달됩니다. ApiError는 파싱된 RFC 9457 Problem Details를 담고 있습니다:
| 멤버 | 타입 | 설명 |
|---|---|---|
status | number | HTTP 상태 코드. |
headers | Headers | 응답 헤더(예: X-RateLimit-Reset). |
message | string | 사람이 읽을 수 있는 오류 메시지. |
retryable | boolean | API가 재시도 가능으로 표시했는지 여부. |
retryAfter | number | undefined | 권장 재시도 간격(초). |
errorCategory | string | undefined | API 오류 카테고리. |
problem | ProblemDetails | 파싱된 원본 Problem Details 객체. |
속도 제한을 고려한 핸들러:
const client = new ApiClient({ baseUrl: "https://app.framedash.dev", apiKey: process.env.FRAMEDASH_API_KEY ?? "", projectId: process.env.FRAMEDASH_PROJECT_ID ?? "", onError: (err: ApiError): never => { if (err.status === 429) { const retryAfter = err.retryAfter ?? err.headers.get("X-RateLimit-Reset"); throw new Error(`Rate limited; retry after ${retryAfter}`); } throw err; },});보안 동작
섹션 제목: “보안 동작”관리자 키가 유출되지 않도록 클라이언트는 몇 가지 안전 장치를 강제합니다:
- HTTPS 전용 베이스 URL. 일반 HTTP는
localhost/ 루프백 개발 엔드포인트를 제외하고 거부되며, 자격 증명이 포함된 URL(https://...@host)도 거부됩니다. - 리다이렉트 불가. API는 프로그래밍 방식의 JSON 요청을 리다이렉트하지 않으므로 모든 3xx는 오류로 처리됩니다. 클라이언트는 리다이렉트 대상으로
X-API-Key헤더를 재전송하지 않습니다. - 호출당 30초 요청 타임아웃(
AbortSignal.timeout사용).
다음 단계
섹션 제목: “다음 단계”- API 개요 — REST 엔드포인트 세부 정보
- CLI 레퍼런스 — 이 클라이언트로 구축된 명령줄 도구
- MCP Server — 이 클라이언트로 구축된 LLM 연동