关于 objectstack-api
objectstack-api 是用于设计和编写 ObjectStack HTTP API 的技能,面向 ObjectStack 开发者,涵盖自动生成的数据路由、声明式端点、访问策略及端点约束。
主要功能
- 01对象的自动生成 REST 路由
- 02通过 apis 声明端点
- 03API 方法与访问策略指引
- 04端点映射和命名规则
- 05公开表单端点约定
适用场景
- 01设计 ObjectStack 对象的 CRUD 和查询路由
- 02声明委托给数据或流程管线的端点
- 03检查匿名表单提交和端点访问
- 04升级含声明式 API 定义的堆栈
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ objectstack-ai/ objectstack --skill objectstack-api安装后在 Claude Code 中输入/objectstack-api确认已加载。
附带文件references/_index.mdevals/endpoints-auth-routes.json
详细介绍
API 设计 — ObjectStack API 协议
技能边界
| 需求 | 改用 |
|---|---|
| 对象、字段、权限或数据源 | objectstack-data |
| 筛选、排序、分页或聚合请求 | objectstack-query |
| 注册内核服务,或从插件读取内核服务 | objectstack-platform |
| 路由守卫或身份验证谓词中的 CEL | objectstack-formula |
自动生成的 API 与自定义 API
自动生成的 API
每个 apiEnabled: true(默认值)的 ObjectStack 对象都会自动获得完整的 REST API:
GET /api/v1/data/{object} # List records (with filter, sort, pagination)
GET /api/v1/data/{object}/:id # Get single record
POST /api/v1/data/{object} # Create record
PATCH /api/v1/data/{object}/:id # Update record
DELETE /api/v1/data/{object}/:id # Delete record (hard delete)
POST /api/v1/data/{object}/query # Complex queries + aggregation (QueryAST in body)
POST /api/v1/data/{object}/batch # Per-object batch operations
POST /api/v1/batch # Cross-object atomic batch
数据 CRUD 位于 /data 前缀下。没有 /bulk 路由,也没有 GET .../aggregate 路由——批量写入通过 batch 端点进行,聚合则通过 POST /api/v1/data/{object}/query 进行,请求体中包含 groupBy/aggregations。
关键规则: 如果对象定义了
apiMethods,则只会公开这些操作(以及由它们派生的内容);[]表示拒绝所有操作。可配置的值是六种基本操作——请参阅 API 方法(操作)。
元数据 API(/meta)
元数据读取接口位于 /api/v1/meta 下(与上文的数据 CRUD 路由分开):
GET /api/v1/meta/:type # List metadata items of a type (object, view, flow, doc, …)
GET /api/v1/meta/:type/:name # Read a single metadata item
读取端的查询参数(?preview=draft、?package=、?include=content)是客户端契约——任何 stack 都不会声明这些参数。
公共(匿名)表单端点
只有当 FormView 的 sharing 同时声明以下三项时,匿名表单端点才会提供该表单:enabled: true(架构默认值为 false)、allowAnonymous: true 和 publicLink slug。只要缺少任何一项,两个端点都会返回 404 FORM_NOT_FOUND。之后,表单会挂载在:
GET /api/v1/forms/:slug # returns form spec + restricted objectSchema
POST /api/v1/forms/:slug/submit # whitelist-filtered INSERT, no auth header
这些端点会绕过 enforceAuth(Web-to-Lead / Web-to-Case 工作流)。表单自身的声明授权提交操作:在其目标对象上授予 publicFormGrant(仅可创建并回读新行,不包含其他操作),且该授权会在解析任何权限集之前确定。提交上下文中的 guest_portal 名称本身不授予任何权限,也不需要权限集;同名权限集仅会影响授权结果的遮罩器。框架会剥离表单 sections[].fields[] 列表之外的字段;目标对象上的 beforeInsert 钩子应写入安全的默认值(status='new'、lead_source='web'、……),并 delete 特权键(owner、internal_notes、……)。在受限模式(group/isolated)下,如果对象受组织列限制,该对象上的表单也会被屏蔽——两个端点都会返回 404,保存/发布时会发出 public-form-intake-unavailable 警告;如果对象的行不属于任何组织,可为其声明 tenancy: { enabled: false },以重新开放表单。完整契约请参阅 node_modules/@objectstack/spec/src/ui/view.zod.ts(FormViewSchema)和 node_modules/@objectstack/spec/src/ui/sharing.zod.ts(包含 enabled / allowAnonymous / publicLink 的 SharingConfigSchema)。
声明式端点(apis:)——无需处理器代码
defineStack({ apis }) 会将 HTTP 端点声明为元数据。声明式端点从协议 17 起即可使用:运行时会匹配 METHOD + path,执行端点的策略键,并委派给内置路由使用的同一套管线——object_operation 委派给 /api/v1/data/{object} 后方的数据管线,flow 委派给 POST /api/v1/automation/{name}/trigger 后方的自动化管线。端点是稳定的 URL 加上现有管线之上的策略层,绝不是第二种执行方式。
在 apis: 和代码处理器之间做选择
| 用途 | 适用情况 |
|---|---|
defineStack({ apis }) | 端点是平台已经执行的某项功能的投影:查询并返回记录,或触发工作流。不需要代码,也没有部署产物,受发布门禁控制。优先选择此方式。 |
| 运行代码的工作流 | 端点需要真正的处理器代码:将 type: 'flow' 端点指向一个工作流,该工作流的 script 节点命名一个栈函数(defineStack({ functions })),或其 connector_action 节点调用一个已声明的连接器(出站)。URL 保持声明式;代码在自动化界面原本运行代码的位置执行。 |
由你控制的发送方发起的入站 Webhook 属于 type: 'api' 工作流
(objectstack-automation → 入站 Webhook(api)触发器):它会验证
ObjectStack 自身的 x-objectstack-signature,将请求加入队列,并返回 202。
应用绝不会挂载到 http.server 上——该服务是平台插件自有的建门原语,而通过代码挂载的路由不会获得调用方身份、身份验证门禁、速率限制、命名空间划分或 OpenAPI 条目。目前有三种情况没有应用界面;请如实说明,不要凭空编造:通过原始请求体上的自有签名验证的第三方回调(钩子只检查
x-objectstack-signature)、流式响应(已声明的端点只返回一个 JSON 信封),以及平台不支持的协议。
import type { ApiEndpoint } from '@objectstack/spec/api';
// The stack declares `manifest: { namespace: 'acme', … }` — required, see below.
export const leadFeed: ApiEndpoint = {
name: 'acme_lead_feed',
path: '/api/v1/apps/acme/leads', // /api/v1/apps/<namespace>/<subpath>
method: 'GET',
summary: 'Lead feed',
type: 'object_operation',
objectParams: { object: 'acme_lead', operation: 'find' },
// `authRequired` omitted → defaults to `true`. Omission is SAFE.
cacheTtlSeconds: 30, // GET-only; rides success answers only
};
路径例外规则(ADR-0121 D1/D2)
已声明的路径必须为 /api/v1/apps/<manifest.namespace>/<subpath>。只有
subpath 可以由你命名,且必须遵循下文的一般命名规则。
必须显式声明 manifest.namespace——绝不会从
manifest.id 派生。发布时,任何超出此路径范围的路径都会被拒绝。
五项发布门禁及对应解决方法
运行时无法提供服务的声明会在发布时被拒绝,一次检查一道门禁。不要死记门禁文本;请阅读拒绝信息,其中包含
修复方法。五项门禁:命名空间(见上文的路径例外规则)、受支持的目标
(script / proxy 在 17.x 中不会执行;object_operation 需要同时设置
objectParams.object 和 .operation;flow 需要 target)、映射(见下文)、策略(见下文)和唯一性(每个栈只能声明一项 METHOD + 路径)。
authRequired 和 D6 配对要求
authRequired 默认为 true,因此省略此项是安全的。只有显式设为
false,才会开放匿名、未经身份验证的执行入口——而 ADR-0121 的 D6 要求同时配置一个已启用的预算:
authRequired: false,
rateLimit: { enabled: true, windowMs: 60_000, maxRequests: 100 },
门禁检查的条件是 rateLimit.enabled === true,而不是是否存在该键:
RateLimitConfigSchema.enabled 本身默认为 false,因此只填写
windowMs / maxRequests 只会声明一个不会计量任何请求的预算。端点预算的计量独立于服务器级别的 server.security.rateLimit。
映射键:仅用于投影
inputMapping / outputMapping 只能按点路径移动和重命名字段,不能做其他操作。inputMapping 映射请求正文,在策略链之后、委派之前应用(因此映射绝不能让调用方绕过
authRequired 或速率限制);outputMapping 仅应用于成功响应正文。编写映射前,有三点需要了解:
transform会被拒绝——不存在转换函数注册表。请在值生成的位置计算该值(工作流或公式字段)。find/get/delete类型的object_operation不会读取请求正文,因此其inputMapping会被拒绝。- 两个条目不能写入相同的目标路径,也不能一个路径位于另一个路径之内
(
x和x.y)。
升级低于 17 的技术栈
面向旧版主版本编写的 apis: 块,即使一个字节都不改,含义也会发生变化——原本不起作用的文档会变成执行入口。升级前,请先完成协议升级指南中的 declarative-apis-endpoints-live 部分;这是一项安全审查,而不是重命名。两个关键步骤:将每个 path 移入 carve-out,并搜索每个条目中的 authRequired: false。
端点命名约定
| 模式 | 用例 | 示例 |
|---|---|---|
/api/v1/data/{object} | 自动生成的集合 | /api/v1/data/accounts |
/api/v1/data/{object}/:id | 自动生成的记录 | /api/v1/data/accounts/abc123 |
/api/v1/{object}/:id/{action} | 对记录执行自定义操作 | /api/v1/cases/:id/close |
/api/v1/{domain}/{action} | 域级操作 | /api/v1/ai/chat |
/api/v1/apps/{namespace}/{subpath} | 声明式 apis: 端点——carve-out,不是任意选择 | /api/v1/apps/acme/leads |
规则:
- 集合路径始终使用复数名词(
accounts,而非account)。 - 多词路径使用 snake_case(
project_tasks,而非projectTasks)。 - 动词仅用于操作,不用于 CRUD(
/close、/approve)。 - 始终添加
/api/v1/前缀以标明版本。
API 方法(操作)
可编写的 ApiMethod 枚举就是六种基本操作。范围更广的 EFFECTIVE 词汇(ApiOperation,14 个值)是权限门控和响应所使用的词汇;其中额外的八个动词绝不会在 apiMethods 中声明:
可编写的基本操作:
| 方法 | 当前 HTTP 接口 | 用途 |
|---|---|---|
get | GET /data/{object}/:id | 获取单条记录 |
list | GET /data/{object} | 按筛选条件、排序和分页列出记录 |
create | POST /data/{object} | 创建新记录 |
update | PATCH /data/{object}/:id | 更新现有记录 |
delete | DELETE /data/{object}/:id | 删除记录 |
bulk | POST /data/{object}/batch | 批量创建/更新/删除(bulk ∧ 子操作) |
派生操作(自动授权,从不手动编写):
| 操作 | 派生自 | 当前 HTTP 接口 |
|---|---|---|
upsert | create ∧ update | 不生成接口 |
aggregate | list | POST /data/{object}/query,带有 groupBy/aggregations |
search | list ∧ searchable | 全局 GET /api/v1/search,不是针对单个对象 |
import | create ∨ update(精确遵循 writeMode) | POST /data/{object}/import |
export | list | GET /data/{object}/export |
history(由 get ∧ trackHistory 派生)仅用于权限门控。restore/purge 不会派生——回收站接口已弃用。在 apiMethods 中声明任何派生动词,都会在解析时被剥除,并显示 FROM → TO 警告。
发现与健康状态
GET /api/v1/health、GET /ready(运行时返回 200/启动中返回 503)和 GET /api/v1/discovery(每个服务的状态)在每次部署中都存在。三者都是响应接口——无需编写。
分发器错误代码
HttpDispatcher 是中央请求路由器;它会返回:
| HTTP 状态 | 错误代码 | 发生时机 |
|---|---|---|
| 404 | ROUTE_NOT_FOUND | 没有路径匹配的路由 |
| 405 | METHOD_NOT_ALLOWED | 路由存在,但不支持该方法 |
| 501 | NOT_IMPLEMENTED | 路由已声明,但处理程序仍是存根 |
| 503 | SERVICE_UNAVAILABLE | 服务已注册,但尚未就绪 |
实时订阅
实时契约采用指针式引用;具体结构请查看规范源码:
node_modules/@objectstack/spec/src/api/realtime.zod.ts—TransportProtocol(websocket|sse|polling)、SubscriptionSchema(id、events[]、transport、可选的channel)、RealtimeEventSchema和RealtimeConfigSchema。node_modules/@objectstack/spec/src/api/websocket.zod.ts— WebSocket 消息协议:订阅/取消订阅消息、事件传递、presence、光标和协同编辑消息,以及 ack/error/ping/pong 帧。
身份验证与授权
RBAC(definePermissionSet)和 RLS 属于 objectstack-data 的范畴,不属于此处;本节介绍端点自身的身份验证键。
身份验证配置
端点没有嵌套的 auth 块,也没有 name / request /
response 字段:身份验证由 RestApiEndpointSchema 上的扁平 public +
permissions 键值对表示;声明式 ApiEndpointSchema 端点(以及
dispatcher)使用 authRequired: boolean(默认为 true)——将其设为
false 时,必须配置 rateLimit(ADR-0121 D6 — 请参阅上文声明式端点 →
authRequired 与 D6 配对规则)。速率限制策略由 RateLimitConfigSchema 定义:
import { RateLimitConfigSchema, type RateLimitConfig } from '@objectstack/spec/shared';
const limit: RateLimitConfig = RateLimitConfigSchema.parse({
enabled: true,
windowMs: 60_000, // time window in milliseconds
maxRequests: 100, // max requests per window
});
身份验证提供方
提供方和登录契约位于
node_modules/@objectstack/spec/src/api/auth.zod.ts:AuthProvider 为
'local' | 'google' | 'github' | 'microsoft' | 'ldap' | 'saml',而
LoginRequestSchema 包含 type(登录方式),以及可选的 email、
username、password、provider 和 redirectTo。接入身份验证流程前,请先查看该文件,了解会话和令牌响应的结构。
数据源
defineDatasource、schemaMode、external 写入门控和
credentialsRef 位于 objectstack-data → rules/datasources.md;驱动程序包以及 Turso Cloud/EE 注意事项位于 objectstack-platform → 驱动程序选择指南;driver ID 的词汇表位于 data/driver/config-registry.zod.ts。
错误封装与代码台账(ADR-0112)
error.code 分为两层:封闭的 StandardErrorCode 目录,以及在各所属包的 api/error-code-ledger.zod.ts 中注册的代码。
ApiErrorSchema.code 会校验这两类代码的并集,因此,未注册的代码会导致解析失败 → 封装一致性测试套件失败 → CI 失败。
该台账只收录框架包。下游仓库应保留自己的台账,并自行组合两项检查——envelopeViolations(body) 用于检查结构,makeApiErrorSchema(yourCodes) 用于检查词汇表(两者均位于
api/contract.zod.ts)。任何仓库都不得发出完全未在任何台账中登记的代码。
最佳实践
- 尽可能使用自动生成的 API。 只有在业务逻辑无法通过 CRUD + 触发器实现时,才创建自定义端点。
- 返回一致的错误结构。 dispatcher 封装为
DispatcherErrorResponseSchema:{ success: false, error: { code, message, httpStatus?, route?, service?, hint? } },其中code是语义字符串,且必须包含code和message。通用 API 错误使用ErrorResponseSchema(errors.zod.ts)。请注意,已发布的数据路由返回的是扁平的{ error, code }响应体(例如,CONCURRENT_UPDATE→ 409,VALIDATION_FAILED→ 400)——不要假定每个错误都会使用success: false封装。 - 采用最小权限身份验证。 每个端点都应明确声明所需权限。
- 有意设计幂等写入。 系统不会生成 upsert 路由,因此外部集成需要按其唯一外部 ID 查询,并分支执行创建或更新——通过
POST /api/v1/data/{object}/batch对这些写入进行分组。
常见陷阱
- 未处理 409 Conflict。 生成的
PATCH /api/v1/data/{object}/:id路由通过两种写法实现乐观并发控制:If-Match标头 或 JSON 请求体中的expectedVersion字段——如果两者都发送,以请求体为准,且令牌通常是客户端读取的updated_at值。两者都不发送会跳过检查;版本不匹配时会返回 409CONCURRENT_UPDATE;带引号的空实体标签("")会被拒绝,并返回 400VALIDATION_FAILED,而不是被视为未提供。 - 认为
DELETE可以恢复。 ObjectStack 的DELETE是硬删除——没有回收站(已废弃的enable.trash标志已在 16.x 中移除)。如需支持恢复,请使用针对每个字段的trackHistory(审计跟踪)或lifecycle归档策略,而不是自定义软删除逻辑。
验证你的工作
添加 *.endpoint.ts、自定义路由或身份验证提供程序后,在报告完成之前运行作者时检查:
os validate # Zod schema + CEL predicate validation + bindings (no artifact)
# or: os build # the same gates, plus emits dist/
路由守卫和身份验证谓词使用 CEL;检查会解析这些谓词,并在出错时以非零状态退出,同时指出错误位置,而不是让格式错误的守卫在运行时被忽略。在脚手架生成的项目中,该检查命令为 npm run validate。完整的检查列表请参见 objectstack-platform → 验证你的工作。
参考
完整的 Zod schema 列表(含单行说明)请参见 references/_index.md——其中列出了 node_modules/@objectstack/spec/src/ 中的对应内容。要确认确切的字段结构,请始终Read源代码;不要依赖对属性名称的记忆。