关于 vtex-io-service-configuration-apps
面向 VTEX IO 开发者的编码技能,用于通过独立应用向服务传递结构化配置。内容涵盖基于 schema 的契约、运行时注入,以及通过 `ctx.vtex.settings` 读取配置。
主要功能
- 01分离服务与配置应用的职责
- 02使用 JSON Schema 定义配置
- 03通过 VTEX IO 上下文注入配置
- 04从 `ctx.vtex.settings` 读取设置
- 05显式开放配置入口
适用场景
- 01在多个服务或应用间共享配置
- 02将配置变更与服务发布周期分离
- 03为服务路由或 GraphQL 查询配置参数
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vtex/ skills --skill vtex-io-service-configuration-apps安装后在 Claude Code 中输入/vtex-io-service-configuration-apps确认已加载。
详细介绍
服务配置应用
此技能的适用场景
当 VTEX IO 服务需要通过 configuration builder 从其他应用接收结构化配置,而不是仅依赖本地应用设置时,请使用此技能。
- 使用
configurationbuilder 创建配置应用 - 从服务应用公开配置入口
- 在多个服务或应用之间共享同一配置契约
- 将配置生命周期与运行时应用生命周期分离
- 通过
ctx.vtex.settings读取注入的配置
以下情况不要使用此技能:
- 仅通过
settingsSchema管理的简单应用本地配置 - 通过
contentSchemas.json配置的 Store Framework 区块设置 - 与配置无关的通用服务运行时接线
- 超出配置所需权限范围的策略设计
决策规则
- 将服务应用和配置应用视为职责分离的两个部分。
- 服务应用负责运行时(
node、graphql等),在manifest.json中声明configurationbuilder,定义configuration/schema.json,并通过ctx.vtex.settings读取注入的值。 - 配置应用不负责服务运行时。它不应声明
node或graphqlbuilders,通常只使用configurationbuilder。 - 配置应用通过
configuration字段指向目标服务,并在<service-app>/configuration.json中提供具体值。 - 当配置契约需要独立于使用它的应用时,使用配置应用。
- 当多个应用或服务需要共享同一配置模型时,优先使用配置应用。
- 在服务应用中,如果服务需要从配置应用接收配置,请通过
node/service.json路由或事件中的settingsType: "workspace",或通过 GraphQL 中的@settings,显式公开配置入口。 - 在配置应用中,
configuration/下的文件夹名称必须与configuration字段中的键匹配,且应对应目标服务应用 ID,例如vendor.shipping-service中的shipping-service。 configuration.json的结构必须符合服务应用声明的 JSON Schema。- 在服务运行时中,从
ctx.vtex.settings读取收到的配置,而不是仅为获取这些值自行发起 HTTP 请求。 - 处理程序和解析器应将
ctx.vtex.settings强制转换为或验证为符合配置架构的结构,并应用与该架构一致的默认值。 - 将配置应用视为通过 VTEX IO 上下文注入结构化运行时配置的方式,而非任意运营数据存储的替代方案。
- 当配置仅适用于单个应用,且应直接在 Apps > App Settings 中编辑时,使用
settingsSchema。当配置契约需要共享、版本化或与使用它的应用生命周期解耦时,使用配置应用。 - 如果通过配置应用配置的服务因权限问题无法解析工作区应用配置,请明确评估该场景是否需要在 manifest 中添加
read-workspace-apps策略。不要默认将此策略添加到无关服务。 - 对于服务配置契约,优先使用带有
additionalProperties: false的封闭架构;当结构变得更复杂时,使用definitions和$ref。
硬性约束
约束:服务应用必须显式选择接收配置
服务应用必须使用 settingsType: "workspace" 在 node/service.json 的路由或事件中声明配置可以注入的位置,或者在 GraphQL 中使用 @settings 指令。
原因
配置应用不会自动应用于所有服务入口。服务必须显式标记哪些路由、事件或查询会解析运行时配置。
检测
如果预期服务会接收配置,但其路由、事件或 GraphQL 查询未声明 settingsType 或 @settings,请停止并先明确配置边界。
正确
{
"routes": {
"status": {
"path": "/_v/status/:code",
"public": true,
"settingsType": "workspace"
}
}
}
错误
{
"routes": {
"status": {
"path": "/_v/status/:code",
"public": true
}
}
}
约束:必须使用显式 schema 文件定义配置结构
配置应用及其配置的服务必须使用显式 schema 文件,而不是隐式或未记录的负载。
原因
如果没有 configuration/schema.json 和与之匹配的 configuration.json 契约,跨应用共享的配置就会变得含糊且容易出错。
检测
如果引入配置应用时没有清晰的 schema 文件,或者服务接受结构松散的配置负载,请停止并先定义 schema。
正确
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/ServiceConfiguration",
"definitions": {
"ServiceConfiguration": {
"type": "object",
"properties": {
"bank": {
"type": "object",
"properties": {
"account": { "type": "string" },
"workspace": { "type": "string", "default": "master" },
"version": { "type": "string" },
"kycVersion": { "type": "string" },
"payoutVersion": { "type": "string" },
"host": { "type": "string" }
},
"required": ["account", "version", "kycVersion", "payoutVersion", "host"],
"additionalProperties": false
}
},
"required": ["bank"],
"additionalProperties": false
}
}
}
错误
{
"anything": true
}
约束:使用注入配置的应用必须从运行时上下文读取配置,不得自行发起额外请求
通过配置应用配置服务时,服务必须从 ctx.vtex.settings 读取注入的值,而不是自行发起临时 HTTP 调用来获取相同的配置。
原因
配置应用的作用是让 VTEX IO 将结构化配置直接注入服务上下文。在此基础上增加自定义 fetch 层会带来不必要的复杂性,也会失去构建器的主要运行时优势。
检测
如果服务已公开 settingsType 或 @settings,但仍会自行发起后端 fetch 来获取相同的配置,请停止并改为从 ctx.vtex.settings 读取。
正确
export async function handleStatus(ctx: Context) {
const settings = ctx.vtex.settings
const code = ctx.vtex.route.params.code
const status = resolveStatus(code, settings)
ctx.body = { status }
}
错误
export async function handleStatus(ctx: Context) {
const settings = await ctx.clients.partnerApi.getSettings()
ctx.body = settings
}
推荐模式
将服务和配置应用建模为独立的契约:
- 服务应用公开可解析配置的位置。
- 服务应用在
configuration/schema.json中定义接受的结构。 - 配置应用将服务声明为构建器,并在
configuration.json中提供值。 - 服务通过
ctx.vtex.settings读取注入的配置。
示例:服务应用 vendor.shipping-service
manifest.json:
{
"vendor": "vendor",
"name": "shipping-service",
"version": "1.0.0",
"builders": {
"node": "7.x",
"configuration": "1.x"
}
}
configuration/schema.json:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/ShippingConfiguration",
"definitions": {
"ShippingConfiguration": {
"type": "object",
"properties": {
"carrierApi": {
"type": "object",
"properties": {
"baseUrl": { "type": "string" },
"apiKey": { "type": "string", "format": "password" },
"timeoutMs": { "type": "integer", "default": 3000 }
},
"required": ["baseUrl", "apiKey"],
"additionalProperties": false
}
},
"required": ["carrierApi"],
"additionalProperties": false
}
}
}
示例:配置应用 vendor.shipping-config
manifest.json:
{
"vendor": "vendor",
"name": "shipping-config",
"version": "1.0.0",
"builders": {
"configuration": "1.x"
},
"configuration": {
"shipping-service": "1.x"
}
}
configuration/shipping-service/configuration.json:
{
"carrierApi": {
"baseUrl": "https://api.carrier.com",
"apiKey": "secret-api-key-here",
"timeoutMs": 5000
}
}
示例:读取注入配置的 Node 服务
export async function createShipment(ctx: Context, next: () => Promise<void>) {
const settings = ctx.vtex.settings as {
carrierApi: {
baseUrl: string
apiKey: string
timeoutMs?: number
}
}
const timeoutMs = settings.carrierApi.timeoutMs ?? 3000
const response = await ctx.clients.carrier.createShipment({
baseUrl: settings.carrierApi.baseUrl,
apiKey: settings.carrierApi.apiKey,
timeoutMs,
payload: ctx.state.shipmentPayload,
})
ctx.body = response
await next()
}
示例:使用 @settings 的 GraphQL 查询
type ShippingStatus {
orderId: ID!
status: String!
}
type Query {
shippingStatus(orderId: ID!): ShippingStatus
@settings(type: "workspace")
}
export const resolvers = {
Query: {
shippingStatus: async (_: unknown, args: { orderId: string }, ctx: Context) => {
const settings = ctx.vtex.settings as {
carrierApi: { baseUrl: string; apiKey: string }
}
return ctx.clients.carrier.getStatus({
baseUrl: settings.carrierApi.baseUrl,
apiKey: settings.carrierApi.apiKey,
orderId: args.orderId,
})
},
},
}
服务配置应用的最低工作清单:
- 服务应用在
manifest.json中声明configuration构建器。 - 服务应用定义有效的
configuration/schema.json。 - 配置应用提供
<service-app>/configuration.json,其中的值与 schema 兼容。 - 需要配置的服务路由或事件声明
settingsType: "workspace"。 - 如果流程依赖工作区应用解析,服务清单会评估是否需要
read-workspace-apps。
当配置应由 VTEX IO 运行时共享、版本化并注入,而非由服务代码临时获取时,请使用此方法。
常见失败模式
- 实际需求是在多个应用之间共享配置契约,却使用了应用设置。
- 创建配置应用时,没有提供明确的架构文件。
- 忘记在应接收配置的服务中设置
settingsType或@settings。 - 配置已注入
ctx.vtex.settings,却仍通过 HTTP 获取配置。 - 将配置应用当作通用的运营数据存储。
审查清单
- 是否真的需要配置应用,而不是使用普通的
settingsSchema? - 能否通过本地应用设置和
settingsSchema解决此问题,而无需单独的配置应用? - 服务是否通过
settingsType或@settings明确选择启用配置解析? - 通过服务路由或事件注入配置时,是否在需要的位置声明了
settingsType: "workspace"? - 配置契约是否通过
configuration/schema.json定义,并与configuration.json保持一致? - 服务是否从
ctx.vtex.settings读取配置,而不是自行发起额外的获取请求? - 如果工作流依赖于读取已安装的工作区应用或其配置,是否经过审慎评估后才使用
read-workspace-apps,而不是默认添加? - 配置架构是否保持封闭且足够明确,以便作为共享契约?
- 配置契约是否与运营数据存储明确区分?