vtex-io-app-settings

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

App Settings & Configuration Boundaries

应用设置与配置边界

When this skill applies

适用场景

Use this skill when deciding or implementing how a VTEX IO app should expose configurable settings.
  • Defining
    settingsSchema
  • Adding merchant-configurable app behavior
  • Reviewing whether configuration belongs in app settings or custom data
  • Reading and validating settings in app code
Do not use this skill for:
  • runtime infrastructure settings in
    service.json
  • Master Data entity design
  • policy declaration details
  • route auth modeling
当你需要决策或实现VTEX IO应用对外暴露可配置设置的方案时,可参考本指南:
  • 定义
    settingsSchema
  • 实现商户可配置的应用行为
  • 判断配置应该归属于应用设置还是自定义数据
  • 在应用代码中读取和验证设置
本指南不适用于以下场景:
  • service.json
    中的运行时基础设施设置
  • Master Data实体设计
  • 策略声明细节
  • 路由鉴权模型设计

Decision rules

决策规则

  • Use app settings for stable configuration that merchants or operators should be able to manage explicitly.
  • Use
    settingsSchema
    for app-level configuration managed through VTEX Admin, and use
    store/contentSchemas.json
    for Store Framework block configuration that varies by page or block instance.
  • If the value is global for the app or account, it usually belongs in
    settingsSchema
    . If it varies per page, block, or theme composition, it usually belongs in
    contentSchemas.json
    .
  • Do not use app settings as a substitute for high-volume operational data storage.
  • Use JSON Schema explicitly with
    properties
    ,
    required
    ,
    default
    ,
    enum
    ,
    format
    , and related constraints instead of a generic root
    type: object
    only.
  • Use
    settingsSchema.access: "public"
    only for non-sensitive values that are intentionally safe to expose to frontend code through
    publicSettingsForApp
    .
  • If
    access
    is omitted, do not assume frontend GraphQL consumers can read the settings. Public frontend access must be an explicit choice.
  • Use app settings for API keys, tokens, and secrets instead of hardcoding them in the codebase.
  • Never expose secrets from app settings directly in HTTP responses, GraphQL responses, HTML, or browser-side props.
  • Never expose secrets from app settings in logs either.
  • For sensitive fields such as API keys or passwords, keep them as
    type: "string"
    and consider marking them with
    format: "password"
    . Some platform consumers, such as Apps GraphQL when using
    hidePasswords
    , may use this metadata to mask values in responses. Do not rely on this as the only security layer: secrets must still be treated as backend-only and never exposed in responses or logs.
  • UI-specific hints such as
    ui:widget: "password"
    may be supported by some renderers, but they are not part of the core JSON Schema guarantees. Do not assume the standard VTEX Admin App Settings UI will enforce them.
  • Read backend settings through
    ctx.clients.apps.getAppSettings(ctx.vtex.appId ?? process.env.VTEX_APP_ID)
    and centralize normalization or validation in a helper instead of spreading ad hoc access patterns through handlers.
  • When reading or saving this app's own settings at runtime, use the correct app identifier such as
    process.env.VTEX_APP_ID
    or
    ctx.vtex.appId
    and rely on the app token plus standard app-settings permissions. Do not declare extra License Manager policies in
    manifest.json
    or add workspace-wide policies such as
    read-workspace-apps
    or undocumented policies such as
    write-workspace-apps
    just to "fix" a 403.
  • Pixel apps that need configuration should also consume settings through
    ctx.clients.apps.getAppSettings(...)
    on the backend side of the pixel app. If a value must be available to injected JavaScript, expose only non-sensitive fields through
    access: "public"
    and
    publicSettingsForApp
    , keeping secrets strictly on the server side.
  • Make code resilient to missing or incomplete settings by validating or applying defaults at the consumption boundary.
  • Never assume settings are identical across accounts or workspaces. Each workspace may have different app configuration during development, rollout, or debugging.
Settings vs configuration builder:
  • Use
    settingsSchema
    when the configuration is specific to this app and the merchant is expected to edit it in Apps > App Settings.
  • Consider a separate app using the
    configuration
    builder when the configuration contract needs to be shared across multiple apps, managed separately from the runtime app lifecycle, or injected directly into service context through
    ctx.vtex.settings
    .
  • Prefer a configuration app when the main goal is structured service configuration delivered through VTEX IO runtime context, instead of settings fetched ad hoc by the app itself.
  • 应用设置用于存放商户或运营人员需要显式管理的稳定配置项。
  • 通过VTEX Admin管理的应用级配置使用
    settingsSchema
    定义,随页面或块实例变化的Store Framework块配置使用
    store/contentSchemas.json
    定义。
  • 如果配置值对应用或账号是全局通用的,通常应该放在
    settingsSchema
    中;如果配置值随页面、块或主题组合变化,通常应该放在
    contentSchemas.json
    中。
  • 不要将应用设置用作高体量运营数据的存储方案。
  • 显式使用JSON Schema的
    properties
    required
    default
    enum
    format
    等约束字段,不要只定义泛型的根级别
    type: object
  • 仅当非敏感值需要通过
    publicSettingsForApp
    安全暴露给前端代码时,才将
    settingsSchema.access
    设置为
    "public"
  • 如果未声明
    access
    ,不要假设前端GraphQL消费者可以读取设置,前端公共访问权限必须显式声明。
  • API密钥、令牌、密钥等敏感信息要存放在应用设置中,不要硬编码在代码库中。
  • 绝对不要直接在HTTP响应、GraphQL响应、HTML或浏览器端props中暴露应用设置里的密钥。
  • 也绝对不要在日志中暴露应用设置里的密钥。
  • 对于API密钥或密码等敏感字段,将其类型设置为
    type: "string"
    ,并考虑标记
    format: "password"
    。部分平台消费者(比如开启
    hidePasswords
    的Apps GraphQL)会使用该元数据在响应中屏蔽敏感值,但不要将其作为唯一的安全层:密钥仍然应该仅在后端使用,永远不要暴露在响应或日志中。
  • 部分渲染器可能支持
    ui:widget: "password"
    这类UI专属提示,但它们不属于JSON Schema的核心保障范围,不要假设标准VTEX Admin应用设置UI会强制执行这些规则。
  • 后端通过
    ctx.clients.apps.getAppSettings(ctx.vtex.appId ?? process.env.VTEX_APP_ID)
    读取设置,将配置的归一化或验证逻辑集中在工具函数中,不要在处理器中分散使用临时的访问逻辑。
  • 运行时读取或保存应用自身的设置时,使用正确的应用标识符(比如
    process.env.VTEX_APP_ID
    ctx.vtex.appId
    ),依赖应用令牌和标准的应用设置权限即可。不要为了“修复”403错误就在
    manifest.json
    中声明额外的License Manager策略,也不要添加
    read-workspace-apps
    这类工作区级别的策略,或者
    write-workspace-apps
    这类非公开策略。
  • 需要配置的Pixel应用也应该在后端通过
    ctx.clients.apps.getAppSettings(...)
    读取设置。如果某个值需要提供给注入的JavaScript使用,仅通过
    access: "public"
    publicSettingsForApp
    暴露非敏感字段,密钥必须严格保留在服务端。
  • 在配置使用边界对设置进行验证或应用默认值,让代码能够适配缺失或不完整的配置。
  • 永远不要假设配置在不同账号或工作空间中是一致的,开发、灰度或调试阶段,每个工作空间可能有不同的应用配置。

Hard constraints

应用设置 vs 配置构建器:

Constraint: Configurable app behavior must have a schema

Merchant-configurable settings MUST be modeled through an explicit schema instead of ad hoc unvalidated objects.
Why this matters
Without a schema, configuration becomes ambiguous, harder to validate, and easier to break across environments.
Detection
If code depends on app-level configuration but no schema or validation contract exists, STOP and define it first.
Correct
json
{
  "settingsSchema": {
    "title": "My App Settings",
    "type": "object",
    "properties": {
      "enableModeration": {
        "title": "Enable moderation",
        "type": "boolean",
        "default": false,
        "description": "If true, new content will require approval before going live."
      },
      "apiKey": {
        "title": "External API key",
        "type": "string",
        "minLength": 1,
        "description": "API key for the external moderation service.",
        "format": "password"
      },
      "mode": {
        "title": "Mode",
        "type": "string",
        "enum": ["sandbox", "production"],
        "default": "sandbox"
      }
    },
    "required": ["apiKey"]
  }
}
Wrong
typescript
const settings = ctx.state.anything
  • 当配置是当前应用专属,且商户需要在「应用 > 应用设置」中编辑时,使用
    settingsSchema
  • 当配置协议需要在多个应用间共享、需要与运行时应用生命周期分开管理,或者需要通过
    ctx.vtex.settings
    直接注入到服务上下文时,可以开发单独的应用使用
    configuration
    构建器实现。
  • 如果核心需求是通过VTEX IO运行时上下文提供结构化的服务配置,而非应用自行拉取配置,优先使用配置应用。

Constraint: Sensitive settings must stay backend-only and must not be exposed to the frontend

硬性约束

约束:可配置的应用行为必须有对应的Schema

Secrets stored in app settings such as API keys, tokens, or passwords MUST be treated as backend-only configuration.
Why this matters
App settings are a natural place for secrets, but exposing them in HTTP responses, GraphQL payloads, HTML, or frontend props turns configuration into a security leak.
Detection
If a route, resolver, or frontend-facing response returns raw settings or includes sensitive fields from settings, STOP and move the external call or secret usage fully to the backend boundary.
Correct
typescript
const { apiKey } = await ctx.clients.apps.getAppSettings(
  ctx.vtex.appId ?? process.env.VTEX_APP_ID
)
const result = await externalClient.fetchData({ apiKey })

ctx.body = result
Wrong
typescript
const settings = await ctx.clients.apps.getAppSettings(
  ctx.vtex.appId ?? process.env.VTEX_APP_ID
)
ctx.body = settings
商户可配置的设置必须通过显式Schema定义,不能使用未验证的临时对象。
重要性 没有Schema的配置会变得模棱两可、更难验证,也更容易在不同环境中出现问题。
检查方式 如果代码依赖应用级配置,但没有对应的Schema或验证协议,立即停止开发,先定义好配置Schema。
正确示例
json
{
  "settingsSchema": {
    "title": "My App Settings",
    "type": "object",
    "properties": {
      "enableModeration": {
        "title": "Enable moderation",
        "type": "boolean",
        "default": false,
        "description": "If true, new content will require approval before going live."
      },
      "apiKey": {
        "title": "External API key",
        "type": "string",
        "minLength": 1,
        "description": "API key for the external moderation service.",
        "format": "password"
      },
      "mode": {
        "title": "Mode",
        "type": "string",
        "enum": ["sandbox", "production"],
        "default": "sandbox"
      }
    },
    "required": ["apiKey"]
  }
}
错误示例
typescript
const settings = ctx.state.anything

Constraint: Public app settings access must never expose sensitive configuration

约束:敏感设置必须仅在后端使用,不得暴露给前端

If
settingsSchema.access
is set to
public
, the exposed settings MUST contain only values that are safe to ship to frontend code through
publicSettingsForApp
.
Why this matters
access: "public"
is a delivery choice, not a security control. Once settings are publicly exposed, storefront or frontend code can read them, so secrets and backend-only configuration must never be included there.
Detection
If a settings schema marks access as public and includes API keys, tokens, passwords, or any value intended only for backend integrations, STOP and keep those settings private.
Correct
json
{
  "settingsSchema": {
    "title": "Public Storefront Settings",
    "type": "object",
    "access": "public",
    "properties": {
      "bannerText": {
        "title": "Banner text",
        "type": "string"
      }
    }
  }
}
Wrong
json
{
  "settingsSchema": {
    "title": "My App Settings",
    "type": "object",
    "access": "public",
    "properties": {
      "apiKey": {
        "title": "External API key",
        "type": "string",
        "format": "password"
      }
    }
  }
}
应用设置中存储的API密钥、令牌、密码等密钥必须作为后端专属配置处理。
重要性 应用设置是存储密钥的合理位置,但如果将其暴露在HTTP响应、GraphQL payload、HTML或前端props中,会导致配置信息泄露,产生安全风险。
检查方式 如果路由、解析器或面向前端的响应返回了原始设置,或者包含设置中的敏感字段,立即停止开发,将外部调用或密钥的使用逻辑完全移到后端边界内。
正确示例
typescript
const { apiKey } = await ctx.clients.apps.getAppSettings(
  ctx.vtex.appId ?? process.env.VTEX_APP_ID
)
const result = await externalClient.fetchData({ apiKey })

ctx.body = result
错误示例
typescript
const settings = await ctx.clients.apps.getAppSettings(
  ctx.vtex.appId ?? process.env.VTEX_APP_ID
)
ctx.body = settings

Constraint: Settings must not be used as operational data storage

约束:公共应用设置访问绝对不能暴露敏感配置

App settings MUST represent configuration, not high-volume mutable records.
Why this matters
Settings are for configuration boundaries, not for transactional or large-scale operational data.
Detection
If a proposed setting stores records that behave like orders, reviews, logs, or queue items, STOP and move that concern to a more appropriate data mechanism.
Correct
json
{
  "enableModeration": true
}
Wrong
json
{
  "allReviews": []
}
如果
settingsSchema.access
设置为
public
,暴露的设置只能包含可以安全通过
publicSettingsForApp
发送给前端代码的值。
重要性
access: "public"
是一种分发配置的选择,而非安全控制手段。一旦设置被公开暴露,门店前端或前端代码都可以读取,因此密钥和后端专属配置绝对不能包含在公共设置中。
检查方式 如果设置Schema标记了公开访问,但包含API密钥、令牌、密码或任何仅用于后端集成的值,立即停止开发,将这些设置改为私有。
正确示例
json
{
  "settingsSchema": {
    "title": "Public Storefront Settings",
    "type": "object",
    "access": "public",
    "properties": {
      "bannerText": {
        "title": "Banner text",
        "type": "string"
      }
    }
  }
}
错误示例
json
{
  "settingsSchema": {
    "title": "My App Settings",
    "type": "object",
    "access": "public",
    "properties": {
      "apiKey": {
        "title": "External API key",
        "type": "string",
        "format": "password"
      }
    }
  }
}

Constraint: Code must validate or default settings at the consumption boundary

约束:不得将设置用作运营数据存储

Settings-dependent code MUST tolerate missing or incomplete values safely.
Why this matters
Configuration can drift across workspaces and accounts. Code that assumes every setting is present becomes fragile.
Detection
If code reads settings and assumes required fields always exist with no validation or defaults, STOP and make the dependency explicit.
Correct
typescript
const rawSettings = await ctx.clients.apps.getAppSettings(
  ctx.vtex.appId ?? process.env.VTEX_APP_ID
)
const settings = normalizeSettings(rawSettings)
const enabled = settings.enableModeration ?? false
Wrong
typescript
const enabled = settings.enableModeration.value
应用设置必须用于存储配置,而非高体量的可变记录。
重要性 设置是用于配置边界的,不适用于事务性或大规模运营数据的存储。
检查方式 如果拟新增的设置用于存储订单、评论、日志、队列项这类记录,立即停止开发,将这类数据迁移到更合适的数据存储机制中。
正确示例
json
{
  "enableModeration": true
}
错误示例
json
{
  "allReviews": []
}

Preferred pattern

约束:代码必须在使用边界对设置进行验证或设置默认值

Use settings for stable, merchant-managed configuration, define them with explicit JSON Schema properties, and validate or normalize them where they are consumed. For secrets, keep the read and the external call on the backend and return only the business result to the frontend. Use frontend GraphQL access only for intentionally public settings, and keep backend-only settings behind
getAppSettings(...)
.
依赖设置的代码必须能够安全地处理缺失或不完整的配置值。
重要性 配置在不同工作空间和账号中可能存在差异,假设所有设置都一定存在的代码稳定性会很差。
检查方式 如果代码读取设置后,假设必填字段一定存在,没有做验证或者设置默认值,立即停止开发,显式声明配置依赖规则。
正确示例
typescript
const rawSettings = await ctx.clients.apps.getAppSettings(
  ctx.vtex.appId ?? process.env.VTEX_APP_ID
)
const settings = normalizeSettings(rawSettings)
const enabled = settings.enableModeration ?? false
错误示例
typescript
const enabled = settings.enableModeration.value

Common failure modes

推荐模式

  • Using settings as operational storage.
  • Using only
    type: object
    without explicit
    properties
    and validation details.
  • Reading settings without defaults or validation.
  • Exposing raw settings or secrets to the frontend.
  • Marking settings as
    access: "public"
    when they contain backend-only or sensitive values.
  • Logging settings or secrets in plain text.
  • Hardcoding API keys or tokens instead of storing them in app settings.
  • Adding workspace-level policies such as
    read-workspace-apps
    or invalid policies such as
    write-workspace-apps
    as a generic workaround for app settings permission errors, instead of validating the correct appId and standard app-settings permissions.
  • Using
    settingsSchema
    when the requirement is really block-level Store Framework configuration.
  • Creating schemas that are too broad or vague.
将应用设置用于稳定的、由商户管理的配置,通过显式的JSON Schema属性定义配置,在使用配置的位置对其进行验证或归一化处理。 对于密钥,将读取逻辑和外部调用都放在后端,仅将业务结果返回给前端。 仅对故意公开的设置开放前端GraphQL访问权限,后端专属设置通过
getAppSettings(...)
读取。

Review checklist

常见错误模式

  • Does this data really belong in app settings?
  • Does the
    settingsSchema
    declare explicit
    properties
    with clear types and
    required
    only where necessary?
  • Are sensitive fields represented safely, for example as
    string
    fields with
    format: "password"
    , knowing that some consumers such as Apps GraphQL with
    hidePasswords
    may use that metadata to mask the output?
  • Does the consuming code validate or default missing values?
  • Are secrets kept backend-only and never exposed to the frontend?
  • If
    access: "public"
    is used, are all exposed settings intentionally safe for frontend consumption?
  • Is the settings surface small and intentional?
  • 将设置用作运营存储。
  • 仅定义
    type: object
    ,没有显式声明
    properties
    和验证细节。
  • 读取设置时没有设置默认值或做验证。
  • 将原始设置或密钥暴露给前端。
  • 当设置包含后端专属或敏感值时,仍然标记
    access: "public"
  • 以明文形式记录设置或密钥日志。
  • 硬编码API密钥或令牌,而非存储在应用设置中。
  • 为了临时解决应用设置权限错误,添加
    read-workspace-apps
    这类工作区级别的策略,或者
    write-workspace-apps
    这类无效策略,而不是校验正确的appId和标准应用设置权限。
  • 当需求实际上是块级别的Store Framework配置时,错误使用
    settingsSchema
  • 定义的Schema过于宽泛或模糊。

Reference

审核Checklist

  • 该数据确实适合存放在应用设置中吗?
  • settingsSchema
    是否声明了显式的
    properties
    ,带有清晰的类型,仅在必要时标记
    required
  • 敏感字段是否做了安全处理?比如设置为
    string
    类型并标记
    format: "password"
    ,同时知晓开启
    hidePasswords
    的Apps GraphQL这类消费者可能会使用该元数据屏蔽输出?
  • 配置使用代码是否对缺失的值做了验证或设置了默认值?
  • 密钥是否仅在后端使用,永远不会暴露给前端?
  • 如果使用了
    access: "public"
    ,所有暴露的设置是否确实可以安全地提供给前端使用?
  • 设置的暴露范围是否足够小且符合预期?

参考资料