vtex-io-service-configuration-apps
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseService Configuration Apps
服务配置应用
When this skill applies
适用场景
Use this skill when a VTEX IO service should receive structured configuration from another app through the builder instead of relying only on local app settings.
configuration- Creating a configuration app with the builder
configuration - Exposing configuration entrypoints from a service app
- Sharing one configuration contract across multiple services or apps
- Separating configuration lifecycle from runtime app lifecycle
- Reading injected configuration through
ctx.vtex.settings
Do not use this skill for:
- simple app-local configuration managed only through
settingsSchema - Store Framework block settings through
contentSchemas.json - generic service runtime wiring unrelated to configuration
- policy design beyond the configuration-specific permissions required
当VTEX IO服务需要通过构建器从其他应用接收结构化配置,而非仅依赖本地应用设置时,可使用本规范:
configuration- 使用构建器创建配置应用
configuration - 从服务应用暴露配置入口
- 跨多个服务或应用共享同一配置契约
- 将配置生命周期与运行时应用生命周期分离
- 通过读取注入的配置
ctx.vtex.settings
本规范不适用于以下场景:
- 仅通过管理的简单应用本地配置
settingsSchema - 通过配置的Store Framework区块设置
contentSchemas.json - 与配置无关的通用服务运行时连接逻辑
- 超出配置专属权限范围的策略设计
Decision rules
决策规则
- Treat the service app and the configuration app as separate responsibilities.
- The service app owns runtime (,
node, etc.), declares thegraphqlbuilder inconfiguration, definesmanifest.json, and reads injected values throughconfiguration/schema.json.ctx.vtex.settings - The configuration app does not own the service runtime. It should not declare or
nodebuilders and usually has only thegraphqlbuilder.configuration - The configuration app points to the target service in the field and provides concrete values in
configuration.<service-app>/configuration.json - Use a configuration app when the configuration contract should live independently from the app that consumes it.
- Prefer a configuration app when multiple apps or services need to share the same configuration model.
- In service apps, expose configuration entrypoints explicitly through in
settingsType: "workspace"routes or events, or throughnode/service.jsonin GraphQL when the service should receive configuration from a configuration app.@settings - In configuration apps, the folder name under and the key in the
configuration/field should match the target service app ID, for exampleconfigurationinshipping-service.vendor.shipping-service - The shape of must respect the JSON Schema declared by the service app.
configuration.json - Read received configuration from inside the service runtime instead of making your own HTTP call just to fetch those values.
ctx.vtex.settings - Handlers and resolvers should cast or validate to match the configuration schema and apply defaults consistent with that schema.
ctx.vtex.settings - Treat configuration apps as a way to inject structured runtime configuration through VTEX IO context, not as a replacement for arbitrary operational data storage.
- Use when configuration is local to one app and should be edited directly in Apps > App Settings. Use configuration apps when the contract should be shared, versioned, or decoupled from the consuming app lifecycle.
settingsSchema - If a service configured through a configuration app fails to resolve workspace app configuration due to permissions, explicitly evaluate whether the manifest needs the policy for that scenario. Do not add this policy by default to unrelated services.
read-workspace-apps - For service configuration contracts, prefer closed schemas with and use
additionalProperties: falseplusdefinitionswhen the structure becomes more complex.$ref
- 服务应用与配置应用需划分独立的职责边界。
- 服务应用负责运行时(、
node等),在graphql中声明manifest.json构建器,定义configuration,并通过configuration/schema.json读取注入值。ctx.vtex.settings - 配置应用不负责服务运行时,不应声明或
node构建器,通常仅包含graphql构建器。configuration - 配置应用在字段中指向目标服务,并在
configuration中提供具体配置值。<service-app>/configuration.json - 当配置契约需要独立于消费它的应用存在时,使用配置应用。
- 当多个应用或服务需要共享同一配置模型时,优先使用配置应用。
- 在服务应用中,若需要从配置应用接收配置,需通过路由或事件中的
node/service.json显式暴露配置入口,或在GraphQL中通过settingsType: "workspace"声明。@settings - 在配置应用中,下的文件夹名称和
configuration/字段中的键需与目标服务应用ID匹配,例如configuration中的vendor.shipping-service。shipping-service - 的结构必须符合服务应用声明的JSON Schema要求。
configuration.json - 直接在服务运行时中通过读取接收的配置,不要额外发起HTTP调用获取这些值。
ctx.vtex.settings - 处理函数和解析器需对进行类型转换或验证,使其匹配配置Schema,并应用与Schema一致的默认值。
ctx.vtex.settings - 配置应用的作用是通过VTEX IO上下文注入结构化运行时配置,不能替代任意业务运营数据存储。
- 当配置仅属于单个应用,且需要直接在应用>应用设置中编辑时,使用。当配置契约需要共享、版本管理,或与消费应用的生命周期解耦时,使用配置应用。
settingsSchema - 如果通过配置应用配置的服务因权限问题无法解析工作区应用配置,需显式评估该场景下manifest是否需要策略,不要为不相关的服务默认添加该策略。
read-workspace-apps - 对于服务配置契约,优先使用带的闭合Schema,当结构较复杂时使用
additionalProperties: false加definitions的方式定义。$ref
Hard constraints
硬性约束
Constraint: Service apps must explicitly opt in to receiving configuration
约束:服务应用必须显式声明接收配置
A service app MUST declare where configuration can be injected, using in routes or events, or the directive in GraphQL.
settingsType: "workspace"node/service.json@settingsWhy this matters
Configuration apps do not magically apply to all service entrypoints. The service must explicitly mark which routes, events, or queries resolve runtime configuration.
Detection
If a service is expected to receive configuration but its routes, events, or GraphQL queries do not declare or , STOP and expose the configuration boundary first.
settingsType@settingsCorrect
json
{
"routes": {
"status": {
"path": "/_v/status/:code",
"public": true,
"settingsType": "workspace"
}
}
}Wrong
json
{
"routes": {
"status": {
"path": "/_v/status/:code",
"public": true
}
}
}服务应用必须声明配置的注入位置,可通过路由或事件中的声明,或在GraphQL中使用指令声明。
node/service.jsonsettingsType: "workspace"@settings重要性说明
配置应用不会自动应用到所有服务入口,服务必须显式标记哪些路由、事件或查询需要解析运行时配置。
检测方式
如果预期服务会接收配置,但它的路由、事件或GraphQL查询未声明或,请停止操作,先暴露配置边界。
settingsType@settings正确示例
json
{
"routes": {
"status": {
"path": "/_v/status/:code",
"public": true,
"settingsType": "workspace"
}
}
}错误示例
json
{
"routes": {
"status": {
"path": "/_v/status/:code",
"public": true
}
}
}Constraint: Configuration shape must be defined with explicit schema files
约束:配置结构必须通过显式Schema文件定义
Configuration apps and the services they configure MUST use explicit schema files instead of implicit or undocumented payloads.
Why this matters
Without and matching contracts, shared configuration becomes ambiguous and error-prone across apps.
configuration/schema.jsonconfiguration.jsonDetection
If a configuration app is introduced without a clear schema file or the service accepts loosely defined configuration payloads, STOP and define the schema first.
Correct
json
{
"$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
}
}
}Wrong
json
{
"anything": true
}配置应用和它们配置的服务必须使用显式Schema文件,不能使用隐式或未文档化的载荷。
重要性说明
如果没有和匹配的契约,跨应用的共享配置会变得模糊且容易出错。
configuration/schema.jsonconfiguration.json检测方式
如果引入的配置应用没有清晰的Schema文件,或者服务接收的配置载荷定义松散,请停止操作,先定义Schema。
正确示例
json
{
"$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
}
}
}错误示例
json
{
"anything": true
}Constraint: Consuming apps must read injected configuration from runtime context, not by inventing extra fetches
约束:消费应用必须从运行时上下文读取注入的配置,不要额外发起拉取请求
When a service is configured through a configuration app, it MUST consume the injected values from instead of creating its own ad hoc HTTP call just to retrieve the same configuration.
ctx.vtex.settingsWhy this matters
The purpose of configuration apps is to let VTEX IO inject the structured configuration directly into service context. Adding a custom fetch layer on top creates unnecessary complexity and loses the main runtime advantage of the builder.
Detection
If a service already exposes or but still performs its own backend fetch to retrieve the same configuration, STOP and move the read to .
settingsType@settingsctx.vtex.settingsCorrect
typescript
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 }
}Wrong
typescript
export async function handleStatus(ctx: Context) {
const settings = await ctx.clients.partnerApi.getSettings()
ctx.body = settings
}当服务通过配置应用配置时,必须从中消费注入的值,不要自行发起临时HTTP调用获取相同的配置。
ctx.vtex.settings重要性说明
配置应用的设计目的是让VTEX IO直接将结构化配置注入服务上下文,在之上添加自定义拉取层会造成不必要的复杂度,也丧失了该构建器的核心运行时优势。
检测方式
如果服务已经暴露了或,但仍然自行发起后端拉取请求获取相同配置,请停止操作,改为从读取。
settingsType@settingsctx.vtex.settings正确示例
typescript
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 }
}错误示例
typescript
export async function handleStatus(ctx: Context) {
const settings = await ctx.clients.partnerApi.getSettings()
ctx.body = settings
}Preferred pattern
推荐模式
Model the service and the configuration app as separate contracts:
- The service app exposes where configuration can be resolved.
- The service app defines accepted structure in .
configuration/schema.json - The configuration app declares the service as a builder and supplies values in .
configuration.json - The service reads the injected configuration through .
ctx.vtex.settings
Example: service app
vendor.shipping-servicemanifest.jsonjson
{
"vendor": "vendor",
"name": "shipping-service",
"version": "1.0.0",
"builders": {
"node": "7.x",
"configuration": "1.x"
}
}configuration/schema.jsonjson
{
"$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
}
}
}Example: configuration app
vendor.shipping-configmanifest.jsonjson
{
"vendor": "vendor",
"name": "shipping-config",
"version": "1.0.0",
"builders": {
"configuration": "1.x"
},
"configuration": {
"shipping-service": "1.x"
}
}configuration/shipping-service/configuration.jsonjson
{
"carrierApi": {
"baseUrl": "https://api.carrier.com",
"apiKey": "secret-api-key-here",
"timeoutMs": 5000
}
}Example: Node service consuming injected configuration
typescript
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()
}Example: GraphQL query using
@settingsgraphql
type ShippingStatus {
orderId: ID!
status: String!
}
type Query {
shippingStatus(orderId: ID!): ShippingStatus
@settings(type: "workspace")
}typescript
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,
})
},
},
}Minimum working checklist for service configuration apps:
- The service app declares the builder in
configuration.manifest.json - The service app defines a valid .
configuration/schema.json - The configuration app provides with values compatible with the schema.
<service-app>/configuration.json - Service routes or events that need configuration declare .
settingsType: "workspace" - When the flow depends on workspace app resolution, the service manifest evaluates whether is required.
read-workspace-apps
Use this approach when configuration should be shared, versioned, and injected by VTEX IO runtime rather than fetched ad hoc by service code.
将服务和配置应用建模为独立的契约:
- 服务应用暴露配置的可解析位置。
- 服务应用在中定义可接收的配置结构。
configuration/schema.json - 配置应用将服务声明为构建器,并在中提供配置值。
configuration.json - 服务通过读取注入的配置。
ctx.vtex.settings
示例:服务应用
:
vendor.shipping-servicemanifest.jsonjson
{
"vendor": "vendor",
"name": "shipping-service",
"version": "1.0.0",
"builders": {
"node": "7.x",
"configuration": "1.x"
}
}configuration/schema.jsonjson
{
"$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-configmanifest.jsonjson
{
"vendor": "vendor",
"name": "shipping-config",
"version": "1.0.0",
"builders": {
"configuration": "1.x"
},
"configuration": {
"shipping-service": "1.x"
}
}configuration/shipping-service/configuration.jsonjson
{
"carrierApi": {
"baseUrl": "https://api.carrier.com",
"apiKey": "secret-api-key-here",
"timeoutMs": 5000
}
}示例:Node服务消费注入的配置
typescript
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()
}示例:使用的GraphQL查询
@settingsgraphql
type ShippingStatus {
orderId: ID!
status: String!
}
type Query {
shippingStatus(orderId: ID!): ShippingStatus
@settings(type: "workspace")
}typescript
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 - 配置应用提供了,其值与Schema兼容。
<service-app>/configuration.json - 需要配置的服务路由或事件声明了。
settingsType: "workspace" - 当流程依赖工作区应用解析时,服务manifest已评估是否需要权限。
read-workspace-apps
当配置需要被共享、版本管理,且由VTEX IO运行时注入而非服务代码临时拉取时,请使用本方案。
Common failure modes
常见故障模式
- Using app settings when the real need is a shared configuration contract across apps.
- Creating configuration apps without explicit schema files.
- Forgetting or
settingsTypein the service that should receive configuration.@settings - Fetching configuration over HTTP even though it is already injected in .
ctx.vtex.settings - Treating configuration apps as general-purpose operational storage.
- 当实际需求是跨应用共享配置契约时,仍然使用应用设置。
- 创建配置应用时未提供显式Schema文件。
- 在需要接收配置的服务中遗漏了或
settingsType声明。@settings - 即使配置已经注入到中,仍然通过HTTP拉取配置。
ctx.vtex.settings - 将配置应用当作通用运营存储使用。
Review checklist
评审检查清单
- Is a configuration app really needed instead of plain ?
settingsSchema - Could this case be solved with local app settings and instead of a separate configuration app?
settingsSchema - Does the service explicitly opt in to configuration resolution with or
settingsType?@settings - When configuration is injected through service routes or events, is declared where needed?
settingsType: "workspace" - Is the configuration contract defined through and matched by
configuration/schema.json?configuration.json - Does the service read configuration from instead of inventing extra fetches?
ctx.vtex.settings - If the flow depends on reading installed workspace apps or their configuration, was evaluated intentionally instead of added by default?
read-workspace-apps - Does the configuration schema stay closed and explicit enough for a shared contract?
- Is the configuration contract clearly separate from operational data storage?
- 是否真的需要配置应用,而非普通的就能满足需求?
settingsSchema - 本场景是否可以通过本地应用设置加解决,而非单独的配置应用?
settingsSchema - 服务是否通过或
settingsType显式声明参与配置解析?@settings - 当配置通过服务路由或事件注入时,是否在需要的位置声明了?
settingsType: "workspace" - 配置契约是否通过定义,且与
configuration/schema.json匹配?configuration.json - 服务是否从读取配置,而非额外发起拉取请求?
ctx.vtex.settings - 如果流程依赖读取已安装的工作区应用或其配置,是否是主动评估后才添加权限,而非默认添加?
read-workspace-apps - 配置Schema是否足够闭合、明确,适合作为共享契约?
- 配置契约是否与运营数据存储明确分离?