跳到主要内容

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.jsonOpenAPI 3.0.3 文档(需要认证)
GET /api/health匿名就绪检查 — 数据库 · 内存 · 查询日志
GET /admin/metrics · /ping · /healthcheck · /threads运行指标(需要认证)
未认证的路径返回 401 — 并非匿名

列表中未列出的路径采用 fail-closed 策略,返回 401。允许匿名访问的只有 / · /index.html · /api/health · 静态资源,以及 /api/v1/login — 因为它是发放密钥的入口,所以不能要求密钥。

请求头​

请求头必需值
X-API-Key是已签发的密钥或配置中的默认密钥
X-Lang是PQL · CQL · SQL · TQL — 其他值返回 400
X-Params否参数绑定。URL 编码的 JSON 对象
Content-Type是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,如果在流式传输过程中发生错误,最后一行将是 error 而不是 end。

{"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-longepoch 毫秒 → 时间戳
: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_codeHTTP含义
AUTH_MISSING401缺少 X-API-Key 请求头
AUTH_INVALID401密钥不匹配
PARAM_MISSING400请求头 · 语言 · 参数类型转换失败
QUERY_SYNTAX400拒绝 DDL · 多条语句 · 语法错误
QUERY_DATA400数据约束违反
QUERY_CONSTRAINT409完整性约束
QUERY_DEADLOCK409死锁
QUERY_AUTH401后端存储认证失败
QUERY_CONNECTION503无法连接到后端
QUERY_TIMEOUT504后端响应超时
QUERY_ERROR500其他执行失败
QUERY_UNSUPPORTED400该语言不支持的请求
NOT_FOUND404路径不存在
METHOD_NOT_ALLOWED405方法不存在
UNSUPPORTED_MEDIA_TYPE415Content-Type 不匹配
API_KEY_NOT_CONFIGURED503未设置主密钥 — 并非凭据错误
LOGIN_NOT_CONFIGURED503未设置控制台账户的两个密钥
区分 4xx 和 5xx 是关键

语法 · 约束 · 数据 · 认证属于客户端原因(4xx),而连接 · 超时属于基础设施原因(5xx)。如果混为一谈,调用方就无法区分「我的查询有误」还是「存储已宕机」。服务器日志也按相同标准区分:4xx 记录为 WARN,5xx 记录为 ERROR。

API 密钥有两种​

类型位置有效期
默认密钥配置文件 data.gateway.api.key(渲染后)直到配置更改
已签发密钥通过 POST /api/v1/keys 创建。存储仅保留 salt + SHA-256 摘要仅存在于进程内 — 重启后即消失

比较操作始终为常数时间。

控制台登录并不「签发」密钥

登录成功后,会直接返回配置中的默认密钥。原因在于引导 — 在全新安装中,密钥存储为空,而所有创建密钥的路径都已要求密钥。首位进入者必须携带比存储「更早」存在的凭据,那只能是配置文件中的密钥。

因此,此路径既不读取也不写入密钥存储。这是因为即使数据库宕机,此入口也必须保持开放。

相关文档​