API
엔드포인트
| 메서드 · 경로 | 하는 일 |
|---|---|
POST /api/v1/query | 읽기 질의. 전체 결과를 JSON 배열로. 상한에 걸리면 truncated 플래그 |
POST /api/v1/query/stream | 같은 질의를 NDJSON 으로 — 행마다 한 줄 |
POST /api/v1/update | 쓰기 질의 (TQL 은 거부) |
POST /api/v1/format | 질의문 정렬 · 들여쓰기 |
POST /api/v1/cache | 인메모리 현재값 하나 조회 |
GET·POST·DELETE /api/v1/keys · /keys/{id} | API 키 목록 · 발행 · 폐기 |
GET·POST /api/v1/login | 콘솔 로그인 — /api/v1 아래에서 키 없이 통과하는 유일한 경로 |
GET /api/v1/openapi.json | OpenAPI 3.0.3 문서 (인증 필요) |
GET /api/health | 익명 readiness — DB · 인메모리 · 질의로그 |
GET /admin/metrics · /ping · /healthcheck · /threads | 운영 지표 (인증 필요) |
목록에 없는 경로는 fail-closed 로 401 입니다. 익명으로 여는 것은 / · /index.html · /api/health · 정적 자원, 그리고 /api/v1/login 하나뿐입니다 — 키를 내주는 문이라 키를 요구할 수 없기 때문입니다.
헤더
| 헤더 | 필수 | 값 |
|---|---|---|
X-API-Key | O | 발행된 키 또는 설정의 기본 키 |
X-Lang | O | PQL · CQL · SQL · TQL — 그 밖은 400 |
X-Params | 파라미터 바인딩. URL 인코딩된 JSON 객체 | |
Content-Type | O | application/json |
질의문은 요청 본문 그 자체입니다. JSON 으로 감싸지 않습니다.
읽기 — POST /api/v1/query
curl -X POST -H "X-API-Key: ${PP_DATA_GATEWAY_API_KEY}" -H "X-Lang: PQL" \
-H "Content-Type: application/json" \
-d "SELECT tablename FROM pg_tables WHERE schemaname='pp'" \
https://host:5501/api/v1/query
{"result":"SUCCESS","message":"","truncated":false,
"data":[{"tablename":"mm_asset"},{"tablename":"mm_tag"}]}
truncated 를 무시하지 마십시오결과가 상한(기본 100,000행)에 도달하면 게이트웨이는 더 읽지 않고 끊습니다. truncated 가 true 인데 그것을 안 보면, 잘린 결과를 전체로 착각한 채로 집계하게 됩니다.
스트림 — POST /api/v1/query/stream
큰 결과는 이쪽입니다. 서버가 전체를 메모리에 올리지 않고, 클라이언트도 첫 행을 바로 받습니다.
curl -X POST -H "X-API-Key: ${PP_DATA_GATEWAY_API_KEY}" -H "X-Lang: PQL" \
-H "Content-Type: application/json" \
-d "SELECT tablename FROM pg_tables WHERE schemaname='pp'" \
https://host:5501/api/v1/query/stream
{"row":{"tablename":"mm_asset"}}
{"row":{"tablename":"mm_tag"}}
{"end":{"count":43,"exec_ms":42,"truncated":false}}
응답은 application/x-ndjson 이고, 스트림 중간에 오류가 나면 마지막 줄이 end 가 아니라 error 입니다.
{"error":{"message":"..."}}
end 줄을 반드시 확인하십시오인증은 헤더 단계에서 끝나므로 이 응답은 언제나 200 으로 시작합니다. 그 뒤의 실패는 본문 안에서만 보입니다 — end 를 못 받았으면 결과가 끝난 것이 아니라 연결이 끊긴 것이고, end.truncated 가 true 면 상한에서 잘린 것입니다.
파라미터 바인딩 — X-Params
질의문에 :이름 으로 자리를 만들고, X-Params 에 URL 인코딩된 JSON 객체를 넣습니다.
PARAMS=$(python3 -c "import urllib.parse,json;print(urllib.parse.quote(json.dumps({
'site_id': 'SITE_00001',
'from:ts-long': 1772041960266,
'limit:int': 500
})))")
curl -X POST -H "X-API-Key: ${PP_DATA_GATEWAY_API_KEY}" -H "X-Lang: PQL" \
-H "X-Params: ${PARAMS}" -H "Content-Type: application/json" \
-d "SELECT * FROM pp.mm_tag WHERE site_id = :site_id AND created_at >= :from LIMIT :limit" \
https://host:5501/api/v1/query
키에 :타입 을 붙여 변환을 지정합니다. 붙이지 않으면 JSON 의 타입 그대로 들어갑니다.
| 접미 | 뜻 |
|---|---|
:int · :long · :float · :double | 수 |
:string | 문자열 |
:epoch · :ts-long | epoch 밀리초 → 타임스탬프 |
:ts-string | 날짜 문자열 → 타임스탬프 |
모르는 타입 접미는 400 PARAM_MISSING 입니다 — 조용히 무시하지 않습니다.
X-Params 가 깨져 있으면 400 이지 500 이 아닙니다디코딩할 수 없는 값(%zz)이나 JSON 이 아닌 값은 클라이언트 오류입니다. 예전에는 이것이 500 INTERNAL_ERROR 로 새어 나가 「서버가 고장났다」로 읽혔습니다.
쓰기 — POST /api/v1/update
curl -X POST -H "X-API-Key: ${PP_DATA_GATEWAY_API_KEY}" -H "X-Lang: PQL" \
-H "Content-Type: application/json" \
-d "UPDATE pp.mm_tag SET description = 'reactor inlet' WHERE tag_id = 'TAG_001'" \
https://host:5501/api/v1/update
TQL 은 이 경로에서 거부됩니다 — 시계열 엔진은 게이트웨이를 통한 쓰기를 받지 않습니다.
오류 계약
모든 오류가 같은 봉투로 옵니다.
{"result":"ERROR","error_code":"QUERY_SYNTAX",
"message":"DDL statements are not allowed: DROP"}
error_code | HTTP | 뜻 |
|---|---|---|
AUTH_MISSING | 401 | X-API-Key 헤더가 없다 |
AUTH_INVALID | 401 | 키가 맞지 않는다 |
PARAM_MISSING | 400 | 헤더 · 언어 · 파라미터 타입 변환 실패 |
QUERY_SYNTAX | 400 | DDL 거부 · 다중 구문 · 문법 오류 |
QUERY_DATA | 400 | 데이터 제약 위반 |
QUERY_CONSTRAINT | 409 | 무결성 제약 |
QUERY_DEADLOCK | 409 | 교착 |
QUERY_AUTH | 401 | 백엔드 저장소의 인증 실패 |
QUERY_CONNECTION | 503 | 백엔드에 연결하지 못했다 |
QUERY_TIMEOUT | 504 | 백엔드 응답이 늦었다 |
QUERY_ERROR | 500 | 그 밖의 실행 실패 |
QUERY_UNSUPPORTED | 400 | 그 언어가 지원하지 않는 요청 |
NOT_FOUND | 404 | 경로 없음 |
METHOD_NOT_ALLOWED | 405 | 메서드 없음 |
UNSUPPORTED_MEDIA_TYPE | 415 | Content-Type 불일치 |
API_KEY_NOT_CONFIGURED | 503 | 마스터 키가 설정되지 않았다 — 자격증명 실패가 아닙니다 |
LOGIN_NOT_CONFIGURED | 503 | 콘솔 계정 두 키가 설정되지 않았다 |
문법 · 제약 · 데이터 · 인증은 클라이언트 원인(4xx) 이고, 연결 · 타임아웃은 인프라 원인(5xx) 입니다. 하나로 뭉개면 「내 질의가 틀렸나 저장소가 죽었나」를 호출자가 구별할 수 없습니다. 서버 로그도 같은 기준으로 4xx 는 WARN, 5xx 는 ERROR 로 갈라 남깁니다.
API 키는 둘입니다
| 종류 | 어디 있나 | 수명 |
|---|---|---|
| 기본 키 | 설정 파일 data.gateway.api.key (렌더됨) | 설정이 바뀔 때까지 |
| 발행 키 | POST /api/v1/keys 로 만든다. 저장은 salt + SHA-256 다이제스트만 | 프로세스 안에만 삽니다 — 재기동에 사라집니다 |
비교는 언제나 상수 시간입니다.
로그인에 성공하면 설정의 기본 키를 그대로 돌려줍니다. 이유는 부트스트랩입니다 — 클린 설치에서 키 저장소는 비어 있고, 키를 만드는 모든 경로가 이미 키를 요구합니다. 최초 진입자는 저장소보다 «먼저» 존재하는 자격증명을 들고 와야 하고, 그것은 설정 파일의 키뿐입니다.
그래서 이 경로는 키 저장소를 읽지도 쓰지도 않습니다. DB 가 죽어도 반드시 열려야 할 문이기 때문입니다.
관련 문서
- 통제와 한계
- 데이터 게이트웨이 개요
- 데이터 게이트웨이 클라이언트 — 자바 SDK