에러 처리
APIFuse 에러 응답을 읽고 재시도할 실패와 업스트림 서비스가 거절한 요청을 구분하는 방법.
에러 처리
모든 APIFuse 에러 응답은 세 가지를 알려줍니다: 무슨 일이 있었는지(code, message), 누가 요청을 멈췄는지(source), 동일한 재시도가 성공할 수 있는지(retryable). HTTP 상태 코드를 패턴 매칭하는 대신 이 필드들을 사용하세요.
에러 응답 형태
{
"error": {
"code": "UPSTREAM_REJECTED",
"message": "The requested time slot overlaps an existing reservation.",
"retryable": false,
"source": "upstream_rule",
"fix": "Pick a slot that does not overlap the account's existing reservations.",
"requestId": "req_1a2b3c"
}
}| 필드 | 의미 |
|---|---|
code | 안정적인 기계 판독용 코드. 오퍼레이션별 코드는 각 오퍼레이션 레퍼런스 페이지에 나열됩니다. |
message | 사람이 읽는 요약. |
retryable | false면 동일한 재시도는 성공할 수 없습니다 — 요청을 바꾸세요. true면 나중에 재시도하면 성공할 수 있습니다. |
source | 누가 요청을 멈췄는지 (아래 참조). |
fix | 요청을 성공시키려면 무엇을 바꿔야 하는지 (알 수 있는 경우). |
requestId | 지원팀에 문의할 때 함께 전달하세요. |
일부 에러 응답은 source와 retryable을 생략할 수 있습니다. 없을 때는 아래 상태 클래스를 기준으로 처리하세요.
플랫폼 수준 에러(잘못된 Connection ID, 상대 서비스 연결 타임아웃, 취소된 요청)는 같은 필드를 최상위에 담는 평평한 body를 사용합니다: {"error", "code", "message", "action", "retryable", "source", "request_id"}. 공식 SDK는 두 형태를 모두 읽습니다.
누가 요청을 멈췄는가
source | 의미 | 대응 |
|---|---|---|
client | 요청 자체를 바꿔야 합니다 (잘못된 입력, 없거나 만료된 Connection). | 요청을 고치거나 다시 연결한 뒤 호출하세요. |
upstream_rule | 상대 서비스가 요청을 이해했고 자체 규칙에 따라 거절했습니다 — 품절, 겹치는 예약, 계정 상태 제한 등. | 사유를 사용자에게 보여주거나 요청을 바꾸세요. 동일하게 재시도하면 같은 답이 돌아옵니다. |
upstream_failure | 상대 서비스가 오작동했거나 응답하지 않았습니다. | 백오프를 두고 재시도하세요. |
apifuse | APIFuse가 요청을 완료하지 못했습니다. | 백오프를 두고 재시도하고, 지속되면 requestId와 함께 지원팀에 문의하세요. |
상태 클래스
| 상태 | 분류 | 일반적인 source |
|---|---|---|
400, 401, 404 | 요청을 바꿔야 함 | client |
409, 410, 422 | 상대 서비스가 자체 규칙으로 거절 | upstream_rule |
429 | 레이트 리밋 — Retry-After를 지키세요 | upstream_rule |
502, 504 | 업스트림 오작동 또는 타임아웃 | upstream_failure |
500, 503 | APIFuse 측 결함 | apifuse |
409는 장애가 아닙니다: 상대 서비스는 정상이며 확정적인 답을 준 것입니다. 재시도할 에러가 아니라 애플리케이션 데이터로 다루세요 (예: "이미 예약된 시간대입니다"를 표시).
안전한 재시도
retryable: false응답은 절대 자동 재시도하지 마세요.429는Retry-After간격 후에,502/503/504는 지수 백오프로 재시도하세요.- 공식 TypeScript/Python SDK는 이 규칙을 자동으로 따르며, 던져진 에러에서
error.retryable과error.source를 읽을 수 있습니다.
import { ApiFuseClient, ApiFuseError } from "@apifuse/sdk";
const client = new ApiFuseClient({ apiKey: process.env.APIFUSE_API_KEY! });
async function reserve(input: Record<string, unknown>) {
try {
return await client.call.invoke("catchtable", "reserve", input);
} catch (error) {
if (
error instanceof ApiFuseError &&
error.source === "upstream_rule" &&
error.retryable === false
) {
// 확정적 거절 — 재시도하지 말고 사용자에게 사유를 보여주세요.
// (429 레이트 리밋도 source가 "upstream_rule"이지만 retryable이라
// 위 가드가 재시도 경로에 남겨둡니다.)
return { refused: true, reason: error.message, suggestion: error.fix };
}
throw error;
}
}