commerce-app-storage

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Add Database Storage to a Commerce App

为Commerce应用添加数据库存储

Integrates App Builder Database Storage into an existing Commerce app and scaffolds a runtime action that uses
@adobe/aio-lib-db
to read and write documents. The library is MongoDB-like: data lives in collections of documents, queried with familiar filters.
The db-access code is identical regardless of action type — what differs is how the action is registered and what its handler returns:
  • Web action — HTTP-invokable (
    web: "yes"
    ); returns a response built with the
    responses
    helpers from
    @adobe/aio-commerce-lib-core
    .
  • Event/webhook action — invoked by a Commerce event or webhook; referenced from
    app.commerce.config.ts
    via
    commerce-app-eventing
    (
    runtimeActions
    ) or
    commerce-app-webhooks
    (
    runtimeAction
    ).
将App Builder Database Storage集成到现有Adobe Commerce应用中,并搭建一个使用
@adobe/aio-lib-db
读写文档的runtime action。该库类似MongoDB:数据存储在文档集合中,可通过熟悉的过滤器进行查询。
无论action类型如何,数据库访问代码都是相同的——不同之处在于action的注册方式及其处理程序的返回值:
  • Web action — 可通过HTTP调用(
    web: "yes"
    );使用
    @adobe/aio-commerce-lib-core
    中的
    responses
    辅助函数构建响应。
  • 事件/webhook action — 由Commerce事件或webhook触发调用;通过
    commerce-app-eventing
    runtimeActions
    )或
    commerce-app-webhooks
    runtimeAction
    )在
    app.commerce.config.ts
    中引用。

Prerequisites

前提条件

  • Verify the app is scaffolded and initialized, not merely that the config exists. Require both:
    • app.commerce.config.ts
      present in the project root, and
    • the project initialized — signalled by the generated
      src/commerce-extensibility-1/
      directory and installed
      node_modules
      (the
      @adobe/aio-commerce-lib-app
      dependency).
  • If
    app.commerce.config.ts
    is missing, stop and invoke
    commerce-app-init
    first (it writes the config, then runs init).
  • If the config is present but the project is not initialized (no
    src/commerce-extensibility-1/
    or
    node_modules
    ), run
    npx @adobe/aio-commerce-lib-app init
    before continuing. Init is idempotent — it finds the existing config, skips the interactive prompts, installs dependencies, and generates the project files.
  • Actions and custom installation scripts can be authored in TypeScript only once the project has the TypeScript build setup (
    webpack-config.cjs
    + root
    tsconfig.json
    ) that
    init
    scaffolds for a TypeScript Commerce config — see
    commerce-app-init
    . Otherwise, author them in JavaScript.
  • The App Builder Data Services API (API code
    AppBuilderDataServicesSDK
    ) must be added to the project in the Adobe Developer Console — in every workspace that uses the database (no special license beyond App Builder). Without it, runtime actions cannot authenticate to the database service.
  • 验证应用已搭建并初始化,而不仅仅是存在配置。需同时满足:
    • 项目根目录下存在
      app.commerce.config.ts
      并且
    • 项目已初始化——标志为生成了
      src/commerce-extensibility-1/
      目录且已安装
      node_modules
      (包含
      @adobe/aio-commerce-lib-app
      依赖)。
  • 如果缺少
    app.commerce.config.ts
    ,请停止操作并先调用
    commerce-app-init
    (它会写入配置,然后执行初始化)。
  • 如果配置存在但项目未初始化(无
    src/commerce-extensibility-1/
    node_modules
    ),请在继续前运行
    npx @adobe/aio-commerce-lib-app init
    。初始化是幂等的——它会找到现有配置,跳过交互式提示,安装依赖并生成项目文件。
  • 只有当项目具备
    init
    为TypeScript Commerce配置搭建的TypeScript构建环境(
    webpack-config.cjs
    + 根目录
    tsconfig.json
    )时,才能用TypeScript编写action和自定义安装脚本——详情请见
    commerce-app-init
    。否则,请使用JavaScript编写。
  • App Builder Data Services API(API代码
    AppBuilderDataServicesSDK
    )必须在Adobe Developer Console中添加到项目——在所有使用数据库的工作区中(除App Builder外无需特殊许可)。如果没有它,runtime action无法向数据库服务进行身份验证。

Step 1 — Provision the workspace database

步骤1 — 配置工作区数据库

There is a strict one-to-one relationship between an AIO project workspace and a workspace database. The recommended way to provision it is declaratively in
app.config.yaml
— the database is provisioned (if not already present) on
aio app deploy
:
yaml
application:
  runtimeManifest:
    database:
      auto-provision: true
      region: emea # amer | apac | emea | aus — the single source of truth for the region
Extension-only apps need a workaround. Due to a bug in the
aio app
CLI plugin (not
aio-lib-db
),
aio app deploy
only runs declarative auto-provision when the
application
runtime manifest has at least one package with a runtime action. Apps built purely with
extensions
(the recommended layout per the submission guidelines) have no
application
actions, so deploy silently skips provisioning. Make the
application
block "real enough" for provisioning to run by adding an empty packages map and a
post-app-build
hook that creates the directory the provisioning step expects:
yaml
application:
  hooks:
    post-app-build: "mkdir -p dist/application/actions" # provisioning expects this dir to exist
  runtimeManifest:
    packages: {} # empty map — required by the config schema so the application block validates with no actions
    database:
      auto-provision: true
      region: emea # single source of truth — see the region callout below
For local development, declarative auto-provisioning does not run during
aio app run
/
aio app dev
. Provision once up front with the CLI fallback (self-service, no special permissions). The
aio app db …
commands are only available once the storage CLI plugin is installed:
sh
aio plugins install @adobe/aio-cli-plugin-app-storage
aio app db provision --region <amer|apac|emea|aus>
Region is a single source of truth. The
region
in the manifest
database
block must match the
region
passed to every
initDb({ region })
call (or
AIO_DB_REGION
) — in every action and in the install step (Step 6). A mismatch fails the connection. Changing region is destructive:
aio app db delete
, update
database.region
in the manifest, then re-provision.
AIO项目工作区与工作区数据库之间存在严格的一对一关系。推荐的配置方式是在
app.config.yaml
声明式配置——在执行
aio app deploy
时,数据库会被配置(如果尚未存在):
yaml
application:
  runtimeManifest:
    database:
      auto-provision: true
      region: emea # amer | apac | emea | aus — 区域的唯一数据源
仅扩展应用需要变通方案。由于
aio app
CLI插件(非
aio-lib-db
)存在bug,当
application
运行时清单中至少有一个包含runtime action的包时,
aio app deploy
才会执行声明式自动配置。纯用
extensions
构建的应用(提交指南推荐的布局)没有
application
action,因此部署会静默跳过配置。通过添加空的packages映射和
post-app-build
钩子创建配置步骤所需的目录,使
application
块足够“真实”以执行配置:
yaml
application:
  hooks:
    post-app-build: "mkdir -p dist/application/actions" # 配置步骤期望此目录存在
  runtimeManifest:
    packages: {} # 空映射——配置架构要求无action时application块仍能通过验证
    database:
      auto-provision: true
      region: emea # 唯一数据源——请查看下方区域说明
对于本地开发,执行
aio app run
/
aio app dev
时不会运行声明式自动配置。请提前使用CLI备用方式配置一次(自助式,无需特殊权限)。只有安装了存储CLI插件后,
aio app db …
命令才可用:
sh
aio plugins install @adobe/aio-cli-plugin-app-storage
aio app db provision --region <amer|apac|emea|aus>
区域是唯一数据源。清单
database
块中的
region
必须与每个
initDb({ region })
调用(或
AIO_DB_REGION
)中传入的
region
匹配——包括每个action以及安装步骤(步骤6)。不匹配会导致连接失败。更改区域是破坏性操作:执行
aio app db delete
,更新清单中的
database.region
,然后重新配置。

Step 2 — Install the library

步骤2 — 安装库

sh
npm install @adobe/aio-lib-db
sh
npm install @adobe/aio-lib-db

Step 3 — Understand intent

步骤3 — 明确需求

Gather from the user:
  • Action type: web action or event/webhook action (see the two shapes above).
  • Collection name and the operations needed (insert / find / update / delete).
  • Region: must match the manifest
    database.region
    (see the region callout in Step 1). Pass it to
    init()
    or set
    AIO_DB_REGION
    .
从用户处收集以下信息:
  • Action类型:web action或事件/webhook action(参见上方两种类型)。
  • 集合名称和所需操作(插入/查询/更新/删除)。
  • 区域:必须与清单
    database.region
    匹配(参见步骤1中的区域说明)。将其传入
    init()
    或设置
    AIO_DB_REGION

Step 4 — Register the action

步骤4 — 注册Action

Add the action to a user-defined package in
src/commerce-extensibility-1/ext.config.yaml
(any name except
app-management
, which is reserved).
include-ims-credentials: true
is required on every DB action.
Without it,
aio-lib-db
has no IMS token to authenticate with and the connection fails at runtime (and the app installation fails if the action runs during install). Do not omit this annotation.
yaml
undefined
src/commerce-extensibility-1/ext.config.yaml
的用户定义包中添加该action(名称可以是任意名称,除了保留的
app-management
)。
每个数据库Action都必须设置
include-ims-credentials: true
。如果没有它,
aio-lib-db
将没有IMS令牌进行身份验证,运行时连接会失败(如果action在安装期间运行,应用安装也会失败)。请勿省略此注解。
yaml
undefined

src/commerce-extensibility-1/ext.config.yaml

src/commerce-extensibility-1/ext.config.yaml

runtimeManifest: packages: app-management: # ... auto-generated — do not edit my-app: # any name except "app-management" actions: store-record: function: actions/store-record/index.js # relative to src/commerce-extensibility-1/ runtime: nodejs:24 web: "yes" # "yes" for a web action; "no" for an event/webhook action annotations: include-ims-credentials: true # REQUIRED for aio-lib-db auth

| Field                     | Constraint                                                                        |
| ------------------------- | --------------------------------------------------------------------------------- |
| Package name              | Lowercase alphanumeric + hyphens; never `app-management` (reserved)               |
| `function`                | Path relative to `src/commerce-extensibility-1/` — not `src/...` or root-relative |
| `include-ims-credentials` | Must be `true` — without it `init()` has no IMS token and the connection fails    |
| `web`                     | `"yes"` for HTTP-invokable web actions; `"no"` for event/webhook handlers         |
| Collection name           | Non-empty string; created on first write if it doesn't exist                      |
| Region                    | Must match the manifest `database.region` (`amer` \| `apac` \| `emea` \| `aus`)   |
runtimeManifest: packages: app-management: # ... 自动生成——请勿编辑 my-app: # 除"app-management"外的任意名称 actions: store-record: function: actions/store-record/index.js # 相对于src/commerce-extensibility-1/的路径 runtime: nodejs:24 web: "yes" # web action设为"yes";事件/webhook action设为"no" annotations: include-ims-credentials: true # aio-lib-db身份验证必需

| 字段                     | 约束条件                                                                 |
| ------------------------- | -------------------------------------------------------------------------- |
| 包名称                    | 小写字母数字+连字符;绝不能是`app-management`(保留名称)                  |
| `function`                | 相对于`src/commerce-extensibility-1/`的路径——不是`src/...`或根目录相对路径 |
| `include-ims-credentials` | 必须设为`true`——没有它`init()`将没有IMS令牌,连接会失败                    |
| `web`                     | web action设为`"yes"`;事件/webhook处理程序设为`"no"`                      |
| 集合名称                  | 非空字符串;如果不存在,首次写入时会自动创建                                |
| 区域                      | 必须与清单`database.region`匹配(`amer` \\| `apac` \\| `emea` \\| `aus`)     |

Step 5 — Implement the handler

步骤5 — 实现处理程序

Every handler follows the same lifecycle: resolve IMS auth → mint token →
init
connect
→ use a collection → always
close
in
finally
.
ts
// Web action — src/commerce-extensibility-1/actions/store-record/index.ts
import { buildErrorResponse, ok } from "@adobe/aio-commerce-lib-core/responses";
import {
  getImsAuthProvider,
  resolveImsAuthParams,
} from "@adobe/aio-commerce-lib-auth";
import { init as initDb } from "@adobe/aio-lib-db";

export async function main(params: Record<string, unknown>) {
  let client;
  try {
    // Resolve the injected AIO_COMMERCE_AUTH_IMS_* params, then mint a raw token string.
    const authProvider = getImsAuthProvider(resolveImsAuthParams(params));
    const token = await authProvider.getAccessToken();
    const db = await initDb({ token, region: "emea" }); // must match the manifest database.region
    client = await db.connect();
    const records = client.collection("records");

    const result = await records.insertOne({
      ...(params.document as object),
      createdAt: new Date().toISOString(),
    });
    return ok({ body: { result } });
  } catch (error: any) {
    return buildErrorResponse(error.statusCode || 500, {
      body: { message: error.message },
    });
  } finally {
    if (client) await client.close(); // always close — avoids connection leaks
  }
}
For an event/webhook action the only differences are
web: "no"
in registration and that the payload arrives in
params.data
:
ts
// Event/webhook handler — same init/connect/close lifecycle
export async function main(params: Record<string, unknown>) {
  const data = params.data as Record<string, unknown>;
  let client;
  try {
    const authProvider = getImsAuthProvider(resolveImsAuthParams(params));
    const token = await authProvider.getAccessToken();
    const db = await initDb({ token, region: "emea" });
    client = await db.connect();
    await client
      .collection("orders")
      .insertOne({ orderId: data.order_id, receivedAt: new Date() });
    return ok({ body: { processed: true } });
  } finally {
    if (client) await client.close();
  }
}
See assets/db-action.ts for the full annotated reference covering all CRUD operations and cursor iteration.
每个处理程序都遵循相同的生命周期:解析IMS身份验证 → 生成令牌 →
init
connect
→ 使用集合 → 始终在
finally
close
ts
// Web action — src/commerce-extensibility-1/actions/store-record/index.ts
import { buildErrorResponse, ok } from "@adobe/aio-commerce-lib-core/responses";
import {
  getImsAuthProvider,
  resolveImsAuthParams,
} from "@adobe/aio-commerce-lib-auth";
import { init as initDb } from "@adobe/aio-lib-db";

export async function main(params: Record<string, unknown>) {
  let client;
  try {
    // 解析注入的AIO_COMMERCE_AUTH_IMS_*参数,然后生成原始令牌字符串。
    const authProvider = getImsAuthProvider(resolveImsAuthParams(params));
    const token = await authProvider.getAccessToken();
    const db = await initDb({ token, region: "emea" }); // 必须与清单database.region匹配
    client = await db.connect();
    const records = client.collection("records");

    const result = await records.insertOne({
      ...(params.document as object),
      createdAt: new Date().toISOString(),
    });
    return ok({ body: { result } });
  } catch (error: any) {
    return buildErrorResponse(error.statusCode || 500, {
      body: { message: error.message },
    });
  } finally {
    if (client) await client.close(); // 始终关闭——避免连接泄漏
  }
}
对于事件/webhook action,唯一的区别是注册时
web: "no"
,且负载通过
params.data
传入:
ts
// 事件/webhook处理程序——相同的init/connect/close生命周期
export async function main(params: Record<string, unknown>) {
  const data = params.data as Record<string, unknown>;
  let client;
  try {
    const authProvider = getImsAuthProvider(resolveImsAuthParams(params));
    const token = await authProvider.getAccessToken();
    const db = await initDb({ token, region: "emea" });
    client = await db.connect();
    await client
      .collection("orders")
      .insertOne({ orderId: data.order_id, receivedAt: new Date() });
    return ok({ body: { processed: true } });
  } finally {
    if (client) await client.close();
  }
}
完整带注解的参考示例请见assets/db-action.ts,涵盖所有CRUD操作和游标迭代。

Step 6 — Set up collections and indexes on install

步骤6 — 在安装时设置集合和索引

For an App Management app, create collections and indexes with a custom installation step — a script that runs once when the app is installed from the Commerce Admin, and can be reversed on uninstall. Prefer this over creating them ad-hoc on the first request.
Author the step with
defineCustomInstallationStep
(an
install
handler plus an optional
uninstall
). Inside it, resolve the IMS auth params from
context.params
not
config
— then follow the same init → connect →
close
lifecycle as Step 5, and call
createIndex
on the collection object:
ts
// ./scripts/setup-database.ts — referenced from config as ./scripts/setup-database.ts
import { defineCustomInstallationStep } from "@adobe/aio-commerce-lib-app/management";
import {
  getImsAuthProvider,
  resolveImsAuthParams,
} from "@adobe/aio-commerce-lib-auth";
import { init as initDb } from "@adobe/aio-lib-db";

export default defineCustomInstallationStep({
  install: async (config, context) => {
    let client;
    try {
      // context.params carries the injected IMS credentials — NOT config.
      const authProvider = getImsAuthProvider(
        resolveImsAuthParams(context.params),
      );
      const token = await authProvider.getAccessToken();
      const db = await initDb({ token, region: "emea" }); // must match the manifest database.region
      client = await db.connect();

      const orders = client.collection("held_orders"); // get the collection object first
      await orders.createIndex({ order_id: 1 }, { unique: true }); // createIndex on the collection, not a name string
      return { status: "success" };
    } finally {
      if (client) await client.close(); // always close — avoids connection leaks
    }
  },
  uninstall: async (config, context) => {
    // Tear down your database state here.
    // Leave empty to preserve data across reinstalls.
  },
});
Author the install script as an ES module with
export default
— never
module.exports
.
The installation action loads each step via
import * as step from "<script>"
and reads
step.default
, so the script must default-export the
defineCustomInstallationStep(...)
result. CommonJS breaks this:
module.exports.default
surfaces as
step.default.default
and validation fails. The
script
path must end in
.js
or
.ts
— author it directly in TypeScript, no separate compile step needed.
Register the step in
app.commerce.config.ts
under
installation.customInstallationSteps
. The
script
path points directly at your
.ts
file:
ts
// app.commerce.config.ts
installation: {
  customInstallationSteps: [
    {
      script: "./scripts/setup-database.ts",
      name: "Set up held-orders collection",
      description: "Creates the held_orders collection and a unique index on order_id",
    },
  ],
},
FieldConstraint
script
Path relative to the project root; must be an ES module (
export default
) ending in
.js
or
.ts
name
Non-empty string, ≤ 255 characters; unique across all installation steps
description
Non-empty string, ≤ 255 characters
See assets/setup-database.ts for the full annotated install/uninstall reference.
对于App Management应用,使用自定义安装步骤创建集合和索引——该脚本在从Commerce Admin安装应用时运行一次,卸载时可撤销。相比首次请求时临时创建,推荐使用此方式。
使用
defineCustomInstallationStep
编写步骤(包含
install
处理程序和可选的
uninstall
处理程序)。在其中,从**
context.params
解析IMS身份验证参数——不是
config
——然后遵循与步骤5相同的init → connect →
close
生命周期,并在
集合对象**上调用
createIndex
ts
// ./scripts/setup-database.ts — 在配置中引用为./scripts/setup-database.ts
import { defineCustomInstallationStep } from "@adobe/aio-commerce-lib-app/management";
import {
  getImsAuthProvider,
  resolveImsAuthParams,
} from "@adobe/aio-commerce-lib-auth";
import { init as initDb } from "@adobe/aio-lib-db";

export default defineCustomInstallationStep({
  install: async (config, context) => {
    let client;
    try {
      // context.params携带注入的IMS凭据——不是config。
      const authProvider = getImsAuthProvider(
        resolveImsAuthParams(context.params),
      );
      const token = await authProvider.getAccessToken();
      const db = await initDb({ token, region: "emea" }); // 必须与清单database.region匹配
      client = await db.connect();

      const orders = client.collection("held_orders"); // 先获取集合对象
      await orders.createIndex({ order_id: 1 }, { unique: true }); // 在集合上调用createIndex,而不是集合名字符串
      return { status: "success" };
    } finally {
      if (client) await client.close(); // 始终关闭——避免连接泄漏
    }
  },
  uninstall: async (config, context) => {
    // 在此处清理数据库状态。
    // 留空可在重新安装时保留数据。
  },
});
将安装脚本编写为带有
export default
的ES模块——绝不要使用
module.exports
。安装action通过
import * as step from "<script>"
加载每个步骤,并读取
step.default
,因此脚本必须默认导出
defineCustomInstallationStep(...)
的结果。CommonJS会破坏此逻辑:
module.exports.default
会被框架的
import * as
加载器解析为
step.default.default
,导致验证失败。
script
路径必须以
.js
.ts
结尾——直接用TypeScript编写,无需单独编译步骤。
app.commerce.config.ts
installation.customInstallationSteps
下注册该步骤。
script
路径直接指向你的
.ts
文件:
ts
// app.commerce.config.ts
installation: {
  customInstallationSteps: [
    {
      script: "./scripts/setup-database.ts",
      name: "设置held-orders集合",
      description: "创建held_orders集合并在order_id上创建唯一索引",
    },
  ],
},
字段约束条件
script
相对于项目根目录的路径;必须是带有
export default
的ES模块,以
.js
.ts
结尾
name
非空字符串,≤255字符;在所有安装步骤中唯一
description
非空字符串,≤255字符
完整带注解的安装/卸载参考示例请见assets/setup-database.ts

Step 7 — Validate

步骤7 — 验证

sh
aio app build
A build failure points directly to the offending config field. To exercise the action against the real database, deploy and invoke it (
aio app deploy
).
sh
aio app build
构建失败会直接指向有问题的配置字段。要针对真实数据库测试action,请部署并调用它(
aio app deploy
)。

Best practices

最佳实践

  • Always close connections in a
    finally
    block — leaked connections exhaust resources.
  • Match the region — the manifest
    database.region
    is the single source of truth; every
    init()
    call and the install step must use it (see the region callout in Step 1). A mismatch fails the connection silently from the caller's view.
  • Use projections (
    .project({ field: 1 })
    ) and indexes (
    createIndex
    ) for frequently queried fields; index fields must total ≤ 2048 bytes.
  • Iterate large result sets with cursors (
    for await (const doc of collection.find(...))
    ) instead of
    toArray()
    to bound memory.
  • Don't hardcode the region or secrets — prefer
    AIO_DB_REGION
    and the injected IMS token over inline values.
  • Prefer the most specific Adobe I/O library in runtime actions over the
    @adobe/aio-sdk
    umbrella — e.g.
    @adobe/aio-commerce-lib-auth
    for IMS auth and
    @adobe/aio-lib-core-logging
    for the logger — to keep action bundles small.
  • Set up collections and indexes during installation with a custom installation step (
    defineCustomInstallationStep
    , see Step 6) rather than ad-hoc on the first request — it runs once when the app is installed from the Commerce Admin and is reversible on uninstall. A generic App Builder
    post-app-deploy
    hook is only an alternative when the app is not installed through App Management.
  • 始终在
    finally
    块中关闭连接
    ——泄漏的连接会耗尽资源。
  • 匹配区域——清单
    database.region
    是唯一数据源;每个
    init()
    调用和安装步骤都必须使用它(参见步骤1中的区域说明)。不匹配会从调用者视角静默导致连接失败。
  • 对频繁查询的字段使用投影
    .project({ field: 1 })
    )和索引
    createIndex
    );索引字段总大小必须≤2048字节。
  • 使用游标迭代大型结果集
    for await (const doc of collection.find(...))
    )而非
    toArray()
    ,以限制内存使用。
  • 不要硬编码区域或密钥——优先使用
    AIO_DB_REGION
    和注入的IMS令牌,而非内联值。
  • 在runtime action中优先使用最具体的Adobe I/O库而非
    @adobe/aio-sdk
    umbrella库——例如,使用
    @adobe/aio-commerce-lib-auth
    进行IMS身份验证,使用
    @adobe/aio-lib-core-logging
    进行日志记录——以减小action包大小。
  • 在安装期间通过自定义安装步骤
    defineCustomInstallationStep
    ,参见步骤6)设置集合和索引,而非首次请求时临时创建——它在从Commerce Admin安装应用时运行一次,卸载时可撤销。通用的App Builder
    post-app-deploy
    钩子仅当应用不通过App Management安装时才是替代方案。

Common Issues

常见问题

  • Connection fails despite a valid token: the action is missing
    include-ims-credentials: true
    , or the App Builder Data Services API has not been added to the project in the Adobe Developer Console (see Prerequisites).
  • DB not provisioned after
    aio app deploy
    (extension-only app)
    : a bug in the
    aio app
    CLI plugin (not
    aio-lib-db
    ) skips declarative auto-provision when the
    application
    runtime manifest has no runtime action. Apply the extension-only workaround from Step 1 — add
    packages: {}
    and a
    post-app-build: "mkdir -p dist/application/actions"
    hook under
    application
    — or provision once with the CLI fallback for local dev.
  • Connection fails after a region change: the library region doesn't match the manifest
    database.region
    . Moving regions is destructive —
    aio app db delete
    , update
    database.region
    in the manifest, then re-provision (
    aio app deploy
    , or the CLI fallback for local dev).
  • Querying by
    _id
    from a string returns nothing
    : convert it first —
    new ObjectId(idString)
    from
    bson
    . A raw string never matches the stored
    ObjectId
    .
  • DbError
    vs unexpected error
    : errors thrown by the service have
    name === "DbError"
    ; branch on it to separate database failures from application bugs.
  • Auth fails inside an installation step: resolve the IMS auth params from
    context.params
    (
    resolveImsAuthParams(context.params)
    ) — which carries the injected
    AIO_COMMERCE_AUTH_IMS_*
    credentials — not from
    config
    , which holds no credentials. Use
    @adobe/aio-commerce-lib-auth
    , not
    @adobe/aio-lib-core-auth
    : the latter's
    generateAccessToken
    expects
    clientId
    /
    clientSecret
    directly and cannot consume the injected params.
  • Installation step fails to load (
    must export a default function or object
    )
    : the script was authored as CommonJS. Author it as an ES module with
    export default
    ;
    module.exports
    (or
    module.exports.default
    ) surfaces through the framework's
    import * as
    loader as
    .default.default
    and fails validation.
  • createIndex
    errors or has no effect
    : it must be called on a collection object (
    client.collection("name").createIndex({ field: 1 })
    ), not with a collection-name string. Get the collection first, then call
    createIndex
    on it.
  • 令牌有效但连接失败:action缺少
    include-ims-credentials: true
    ,或者App Builder Data Services API未在Adobe Developer Console中添加到项目(参见前提条件)。
  • 执行
    aio app deploy
    后数据库未配置(仅扩展应用)
    aio app
    CLI插件(非
    aio-lib-db
    )存在bug,当
    application
    运行时清单中没有runtime action时会跳过声明式自动配置。应用步骤1中的仅扩展应用变通方案——在
    application
    下添加
    packages: {}
    post-app-build: "mkdir -p dist/application/actions"
    钩子——或使用本地开发的CLI备用方式配置一次。
  • 更改区域后连接失败:库的区域与清单
    database.region
    不匹配。更改区域是破坏性操作——执行
    aio app db delete
    ,更新清单中的
    database.region
    ,然后重新配置(
    aio app deploy
    ,或本地开发的CLI备用方式)。
  • 通过字符串
    _id
    查询无结果
    :先转换为
    ObjectId
    ——使用
    bson
    中的
    new ObjectId(idString)
    。原始字符串永远不会匹配存储的
    ObjectId
  • DbError
    与意外错误
    :服务抛出的错误
    name === "DbError"
    ;针对此分支可区分数据库故障与应用程序bug。
  • 安装步骤中身份验证失败:从
    context.params
    解析IMS身份验证参数(
    resolveImsAuthParams(context.params)
    )——它携带注入的
    AIO_COMMERCE_AUTH_IMS_*
    凭据——而非从
    config
    解析,
    config
    不包含凭据。使用
    @adobe/aio-commerce-lib-auth
    而非
    @adobe/aio-lib-core-auth
    :后者的
    generateAccessToken
    直接需要
    clientId
    /
    clientSecret
    ,无法使用注入的参数。
  • 安装步骤加载失败(
    must export a default function or object
    :脚本编写为CommonJS格式。请编写为带有
    export default
    的ES模块;
    module.exports
    (或
    module.exports.default
    )会被框架的
    import * as
    加载器解析为
    .default.default
    ,导致验证失败。
  • createIndex
    报错或无效果
    :必须在集合对象上调用它(
    client.collection("name").createIndex({ field: 1 })
    ),而非使用集合名字符串。先获取集合,再在其上调用
    createIndex

Quality Bar

质量标准

  • aio app build
    completes without errors
  • Every user-authored DB action declares
    include-ims-credentials: true
    in its annotations
  • The action closes the client in a
    finally
    block and initializes the library in the region declared in the manifest
    database
    block
  • aio app build
    无错误完成
  • 每个用户编写的数据库Action在注解中声明
    include-ims-credentials: true
  • Action在
    finally
    块中关闭客户端,并在清单
    database
    块声明的区域初始化库

Chaining

链式操作

  • Wire the action to an event — invoke
    commerce-app-eventing
    and reference this action in an event's
    runtimeActions
    .
  • Wire the action to a webhook — invoke
    commerce-app-webhooks
    and reference this action via
    runtimeAction
    .
  • Trigger the action from Admin UI — invoke
    commerce-app-admin-ui
    to add a mass action, order view button, or grid column that invokes this runtime action.
  • 将Action与事件关联——调用
    commerce-app-eventing
    并在事件的
    runtimeActions
    中引用此Action。
  • 将Action与webhook关联——调用
    commerce-app-webhooks
    并通过
    runtimeAction
    引用此Action。
  • 从Admin UI触发Action——调用
    commerce-app-admin-ui
    添加批量操作、订单视图按钮或网格列,以调用此runtime action。

References

参考资料

  • assets/db-action.ts — Full annotated handler: init/connect, CRUD, cursor iteration, and the close lifecycle
  • assets/setup-database.ts — Full annotated custom installation step: install creates a collection and a unique index, uninstall drops it, with the
    context.params
    and
    createIndex
    -on-collection patterns
  • assets/db-action.ts — 完整带注解的处理程序:初始化/连接、CRUD、游标迭代和关闭生命周期
  • assets/setup-database.ts — 完整带注解的自定义安装步骤:安装时创建集合和唯一索引,卸载时删除,包含
    context.params
    和在集合上调用
    createIndex
    的模式",