콘텐츠로 이동

규칙

운영 출처는 https://haneoka.org입니다. 카탈로그 직접 라우트는 다음과 같습니다.

GET /api/v1/{resource}
GET /api/v1/{resource}/{id}
GET /api/v1/{resource}/views/{view}
GET /api/v1/{resource}/views/{view}/{id}
GET /api/v1/{resource}/relations/{relation}/{key}

이 라우트는 기본으로 현재 intl 서버를 씁니다. 다른 활성 서버를 고르려면 ?server=jp처럼 GET /api/v1/releases가 반환하는 슬러그를 붙이세요. 명시적인 /api/v1/servers/{server}/... 형태는 같은 카탈로그 처리기에 닿으며, 재현 가능한 빌드에 쓰는 release 옵션이 더해집니다.

resource, id, view, relation, key는 데이터로 정해지는 값입니다. 라우트를 만들기 전에 카탈로그 매니페스트나 색인 응답을 읽으세요. releases, servers, account, me, community, garupa 같은 최상위 그룹은 자체 API 라우트를 가지며 카탈로그 리소스 이름이 아닙니다. 동적 세그먼트는 각각 URL 인코딩하고, /는 문서화된 라우트가 경로를 받는 곳에서만 그대로 두세요.

직접 라우트 시험:

GET /api/v1/bands
아직 보내지 않음

JSON 응답은 application/json; charset=utf-8을 씁니다. 카탈로그 색인 응답은 보통 엔티티 ID를 키로 하는 객체입니다. 엔티티 값은 원래 DTO 형태를 유지합니다. 예를 들어 곡은 musicId, musicTitle 같은 언어별 배열, 난이도 레코드, 미디어 경로를 쓰고, events 리소스는 { "entries": {}, "hasGameEvents": false }를 반환할 수 있습니다. 요청한 리소스의 문서화된 필드에 맞춰 구현하고 그 밖의 필드는 보존하세요.

API는 프로바이더 DTO에 공통 id, title, data 래퍼를 더하지 않습니다. 값이 리소스별 ID 필드를 쓰더라도 색인 키가 조회용 ID입니다. null 필드는 원본에 현재 그 값이 없다는 뜻입니다.

헤더의미
X-Request-Id오류나 진단 보고를 위한 상관 ID.
X-Haneoka-Release-Id서버 단위 응답을 보낸 현재 릴리스.
X-Haneoka-Source-Id그 릴리스와 연결된 소스 스냅숏.
ETag조건부 요청을 위한 표현 검증자.
Cache-Control서비스가 고른 신선도와 재검증 정책.
Content-Range반환한 바이트 범위, 또는 충족할 수 없는 범위의 bytes */size.

현재 별칭은 서비스가 새 카탈로그를 올리면 바뀔 수 있습니다. 사용자가 어떤 데이터를 봤는지 설명해야 한다면 캐시 항목과 함께 릴리스 헤더를 저장하세요. 안정적인 재현이 필요한 애플리케이션은 명시적 release 요청으로 바꿀 수 있으며, 그 절차는 고급 릴리스 페이지에 있습니다.

배치 요청에서는 id를 반복합니다:

GET /api/v1/songs?id=100001&id=100002

카탈로그 배치는 { "items": { ... }, "missing": [ ... ] }를 반환합니다. 서비스는 캐시 키를 위해 ID를 정렬하고 중복을 없앱니다. 없는 ID는 그 ID에 대한 결과일 뿐 요청 실패가 아닙니다.

카탈로그 매니페스트는 리소스마다 뷰와 관계를 선언합니다. 뷰는 자체 색인과 엔티티 형태를 가집니다. 관계는 ID를 키로 하는 정식 엔티티(valueMode: "ids")나 프로바이더 레코드(valueMode: "records")를 반환합니다. 매니페스트의 이름과 형태를 따르세요. 값은 리소스마다 다를 수 있습니다.

ETag와 Cache-Control을 따르세요. If-None-Match를 보내면 표현이 바뀌지 않았을 때 304를 받습니다. /assets/intl/..., /runtime/intl/... 같은 상대 미디어 경로는 https://haneoka.org 기준으로 해석하세요.

바이너리 라우트는 미디어 타입을 선언하며 단일 바이트 범위를 지원할 수 있습니다. 유효한 부분 응답은 206, 충족할 수 없는 범위는 Content-Range: bytes */<size>와 함께 416입니다. 416을 그대로 재시도하지 마세요.

공개 카탈로그, 미디어, Sonolus, Bestdori 읽기에는 세션이 필요 없습니다. 커뮤니티 쓰기, 프로필 변경, 환경설정, 업로드, Better Auth 작업은 HTTP-only 쿠키와 같은 출처의 브라우저 요청을 씁니다.

await fetch("/api/v1/account/profile", {
credentials: "include",
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ bio: "Hello", version: 4 }),
});

세션 쿠키를 로그나 클라이언트 번들에 넣지 마세요. 인증이 필요한 각 페이지에 그 작업의 요청 본문, 충돌 필드, 모더레이션 상태가 적혀 있습니다.