You need to enable JavaScript to run this app.
文档中心
全站加速

全站加速

复制全文
下载 pdf
开发指南
Data SDK 参考
复制全文
下载 pdf
Data SDK 参考

Data SDK(@iga-pages/data)是 IGA Pages 提供的轻量数据 CRUD SDK。它在应用的 Node.js 服务端运行,通过 IGA Pages 数据服务读写当前项目的数据,提供 create、get、list、update、remove 五个 API。本文说明 SDK 的适用场景、使用前提、数据模型与限制、各 API 的参数和行为,以及错误处理方式。

能力API说明
创建记录create写入一条业务记录,返回数据服务生成的记录 ID。
查询单条记录get按 ID 获取记录;记录不存在时返回 null。
查询记录列表list按业务字段过滤并分页查询。
更新记录update按 ID 更新指定业务字段。
删除记录remove按 ID 删除记录。

概念与适用场景

解决的问题

AI Coding 工具生成的待办、项目、日记、收支等个人工作台类应用,常把数据保存在浏览器 localStorage 中。这类数据只存在于当前浏览器:换一台设备或换一个浏览器就看不到,清理浏览器数据后可能丢失。Data SDK 把这些业务数据保存到数据服务中,让网页 Demo 成为可以长期使用的应用。

基本概念

  • 数据范围是项目:同一个 Pages 项目中的所有调用读写同一批记录。SDK 按项目隔离数据,应用的所有最终用户共享这批记录。需要按用户区分数据时,由应用在记录中保存用户标识字段,并在查询时按该字段过滤。
  • 记录是结构不固定的 JSON 对象:写入前无需建表或定义字段,同一项目中的记录可以有不同的字段。数据服务为每条记录生成 ID、CreatedAt 和 UpdatedAt。
  • 只在服务端调用:SDK 使用 IGA Pages 自动注入的凭证访问数据,应用代码只需导入并调用 API。具体条件见使用前须知。

适用场景

Data SDK 适合保存个人或小团队应用的轻量业务数据,并按字段精确筛选、分页浏览。例如:

  • 待办、日记、收支记录等个人应用数据。
  • 高校社团等小团队共用的数据。

Data SDK 提供单条记录的增删改查和按创建时间排序的分页列表。应用需要以下能力时,请选择其他数据存储方案:

  • 跨记录事务或关联查询。
  • 自定义排序、范围查询或模糊查询。
  • 超出数据模型与限制中记录数或单条记录大小上限的数据量。
  • 从浏览器直接读写数据。
  • 在 Agent 项目中使用。

使用前须知

调用 Data SDK 前,确认项目和运行环境满足以下条件:

  • 项目类型:Data SDK 仅支持 Pages 项目。Agent 项目直接依赖 Data SDK 时,CLI 会在创建或关联项目(iga agents link)以及本地启动、构建和部署前报错。

  • Node.js 版本:Node.js 20 或更高版本。

  • 调用位置:只能在 IGA Pages 服务端运行时中调用,例如:

    • API Handler
    • Next.js Route Handler
    • Server Action
    • 其他 Node.js 服务端模块

    仅在服务端代码中导入 SDK。浏览器需要读写数据时,由应用的服务端接口代为调用,不要在浏览器代码或 "use client" 模块中导入 SDK。

  • 凭证:SDK 自动使用 IGA Pages 注入的 Page SDK Token(环境变量 IGA_AUTH_KEY),应用代码无需配置服务地址或凭证。缺少 Token 时,调用会抛出 ConfigurationError,详见错误处理。

  • 开通:项目首次调用 Data SDK 时,数据服务自动完成数据存储配置,无需单独开通。

安装

在项目根目录执行:

npm install @iga-pages/data

SDK 同时提供 ESM、CommonJS 和 TypeScript 类型声明。

本地开发

本地开发时,先登录并关联项目,再启动本地开发服务:

iga login
iga pages link
iga pages dev

iga pages dev 会为本地服务端运行时注入当前项目的 Page SDK Token。应用代码不需要、也不应该自行读取或传递 Token。

快速开始

以下代码依次创建、查询、分页列出、更新和删除一条记录。将代码放在服务端模块(如 API Handler)中,并通过 iga pages dev 启动本地开发服务后调用:

import {
  create,
  get,
  list,
  update,
  remove,
} from "@iga-pages/data";

const { ID } = await create({
  title: "编写 PRD",
  status: "todo",
});

const record = await get(ID);

const page = await list({
  filter: { status: "todo" },
  pageNumber: 1,
  pageSize: 20,
});

await update(ID, { status: "done" });

await remove(ID);

数据模型与限制

记录结构

每条记录是一个 DataRecord 对象。业务字段可以自由定义,系统字段由数据服务维护:

interface DataRecord {
  ID: string;
  CreatedAt?: string;
  UpdatedAt?: string;
  [field: string]: unknown;
}
字段类型说明
IDstring记录 ID,由数据服务在创建时生成,为 24 位十六进制字符串。
CreatedAtstring创建时间,RFC 3339 格式的 UTC 时间字符串。
UpdatedAtstring最后更新时间,RFC 3339 格式的 UTC 时间字符串。每次 update 后更新。
其他字段unknown业务字段,由应用写入。

系统字段

记录 ID、时间戳和数据归属由数据服务生成和维护。create 和 update 发送请求前,SDK 会从传入对象中移除以下字段:

  • ID
  • CreatedAt
  • UpdatedAt
  • account_id
  • project_id
  • created_at
  • updated_at

字段名与值

写入的数据须满足以下规则,否则数据服务拒绝请求,SDK 抛出 BusinessError:

  • 字段名非空,不以 $ 开头,不包含 .。
  • 顶层字段名不能为 _id。
  • 字段值为 null、布尔值、字符串、数字、对象或数组。

大小与数量限制

限制项上限
单次写入的数据大小(create 的 data 或 update 的 patch,JSON 序列化后)1 MB
单次写入的数据嵌套层数4 层
每个项目的记录数10000 条
list 的 filter 字段数16 个
list 的 filter 大小(JSON 序列化后)32 KB

超出上限时,数据服务拒绝请求,SDK 抛出 BusinessError。项目记录数达到 10000 条后,create 会失败;删除记录后可以继续创建。

API 参考

五个 API 的签名如下:

create(
  data: Record<string, unknown>,
  options?: DataOperationOptions,
): Promise<{ ID: string }>;

get<T extends DataRecord = DataRecord>(
  id: string,
  options?: DataOperationOptions,
): Promise<T | null>;

list<T extends DataRecord = DataRecord>(
  query?: ListQuery,
  options?: DataOperationOptions,
): Promise<ListResult<T>>;

update(
  id: string,
  patch: Record<string, unknown>,
  options?: DataOperationOptions,
): Promise<void>;

remove(
  id: string,
  options?: DataOperationOptions,
): Promise<void>;

所有 API 都支持同一个可选参数 options,用于取消请求,详见取消请求:

interface DataOperationOptions {
  abortSignal?: AbortSignal;
}

create:创建记录

const result = await create(data, options?);

参数

参数类型必填说明
dataRecord<string, unknown>是要写入的业务字段对象。必须是非 null、非数组的对象,并且可以 JSON 序列化。
options.abortSignalAbortSignal否用于取消当前请求。

返回值

Promise<{ ID: string }>

返回数据服务生成的记录 ID。需要完整记录时,用该 ID 调用 get。

行为说明

  • SDK 移除 data 中的系统字段后再写入,详见系统字段。
  • data 不是对象,或包含循环引用、BigInt 等无法 JSON 序列化的值时,SDK 抛出 BadRequest。
  • 字段名、字段值、数据大小或项目记录数不满足数据模型与限制时,SDK 抛出 BusinessError。
  • 数据服务的响应中缺少有效记录 ID 时,SDK 抛出 InvalidResponse。

示例

const { ID } = await create({
  kind: "todo",
  title: "完善 Data SDK 文档",
  priority: "high",
  completed: false,
});

get:查询单条记录

const record = await get<T>(id, options?);

参数

参数类型必填说明
idstring是记录 ID,必须是非空字符串。
options.abortSignalAbortSignal否用于取消当前请求。

返回值

Promise<T | null>
  • 找到记录:返回完整记录。
  • 记录不存在:返回 null。传入格式无效的 ID 时同样返回 null。

行为说明

  • id 不是非空字符串时,SDK 抛出 BadRequest。
  • 泛型 T 只提供 TypeScript 类型提示。SDK 只校验响应外层结构,自定义业务字段的运行时校验由应用自行完成。

示例

interface TodoRecord extends DataRecord {
  kind: "todo";
  title: string;
  completed: boolean;
}

const todo = await get<TodoRecord>(ID);

if (!todo) {
  console.log("记录不存在");
}

list:分页查询记录

const result = await list<T>(query?, options?);

参数

interface ListQuery {
  filter?: Record<string, unknown>;
  pageNumber?: number;
  pageSize?: number;
}
参数类型必填默认值说明
query.filterRecord<string, unknown>否{}按业务字段过滤。每个字段按值精确匹配,多个字段需同时满足。必须是对象,否则 SDK 抛出 BadRequest。字段数和大小限制见大小与数量限制。
query.pageNumbernumber否1页码,从 1 开始。小于等于 0 时按 1 处理。
query.pageSizenumber否20每页条数,最大 100。大于 100 时按 100 返回;显式传入 0 时按 10 返回。
options.abortSignalAbortSignal否—用于取消当前请求。

说明

SDK 调用层使用 filter 组织过滤字段;发送给数据服务时,业务过滤字段会与 PageNumber、PageSize 放在同一层级。因此,filter 中名为 PageNumber 或 PageSize 的字段会被分页参数覆盖,不参与过滤。

返回值

interface ListResult<T extends DataRecord = DataRecord> {
  Items: T[];
  PageNumber: number;
  PageSize: number;
  TotalCount: number;
}
字段说明
Items当前页的记录。
PageNumber实际返回的页码。
PageSize实际使用的每页条数。pageSize 大于 100 时为 100。
TotalCount满足 filter 的记录总数。

行为说明

  • 结果按创建时间升序排列;创建时间相同时,按记录 ID 升序排列。排序方式固定。
  • 读取全部记录时,根据 TotalCount 和 PageSize 计算页数,逐页调用 list。

示例

const result = await list<TodoRecord>({
  filter: {
    kind: "todo",
    completed: false,
  },
  pageNumber: 1,
  pageSize: 50,
});

console.log(result.Items);
console.log(result.TotalCount);

update:更新记录

await update(id, patch, options?);

参数

参数类型必填说明
idstring是记录 ID,必须是非空字符串。
patchRecord<string, unknown>是需要更新的业务字段。必须是非 null、非数组的对象,并且可以 JSON 序列化。
options.abortSignalAbortSignal否用于取消当前请求。

返回值

Promise<void>

update 不返回更新后的记录。需要最新记录时,在更新后调用 get。

行为说明

  • update 按顶层字段替换:patch 中出现的字段整体覆盖原值,未出现的字段保持原值,UpdatedAt 随之更新。
  • 嵌套对象整体替换,不做深度合并。例如原记录为 { profile: { name: "A", age: 1 } },调用 update(ID, { profile: { name: "B" } }) 后,profile 变为 { name: "B" }。只修改嵌套对象中的部分字段时,先用 get 读取记录,在应用中合并后再写回完整的嵌套对象。
  • SDK 移除 patch 中的系统字段后再写入,详见系统字段。
  • id 不是非空字符串、patch 不是对象或无法 JSON 序列化时,SDK 抛出 BadRequest。
  • 记录不存在、ID 格式无效,或 patch 不满足数据模型与限制时,SDK 抛出 BusinessError。

示例

await update(ID, {
  completed: true,
  completedAt: new Date().toISOString(),
});

更新后读取最新记录:

await update(ID, { status: "done" });
const updated = await get(ID);

remove:删除记录

await remove(id, options?);

参数

参数类型必填说明
idstring是记录 ID,必须是非空字符串。
options.abortSignalAbortSignal否用于取消当前请求。

返回值

Promise<void>

行为说明

  • id 不是非空字符串时,SDK 抛出 BadRequest。
  • 记录不存在或 ID 格式无效时,SDK 抛出 BusinessError。

示例

await remove(ID);

取消请求

五个 API 都支持通过 AbortSignal 取消请求:

const controller = new AbortController();

const request = list(
  { pageNumber: 1, pageSize: 100 },
  { abortSignal: controller.signal },
);

controller.abort();

await request;

请求取消后,SDK 抛出 DataError,其 code 为 AbortError。

错误处理

SDK 对参数、运行环境、请求和响应检查中识别到的失败抛出 DataError。使用 instanceof DataError 识别 SDK 错误,通过 error.code 区分原因;其他异常继续抛出:

import { DataError, list } from "@iga-pages/data";

try {
  await list();
} catch (error) {
  if (error instanceof DataError) {
    console.error(error.message);  // 查看失败原因
    console.error(error.toJSON()); // 提供给平台排查
  } else {
    throw error;
  }
}

错误结构:

interface DataErrorInfo {
  code: string;
  status?: number;
  message: string;
}
字段说明
codeSDK 的错误分类,取值见错误码。
status实际 HTTP 状态码。业务失败时为 200;没有 HTTP 响应时省略。
message上游错误原因;有日志 ID 时附带 log_id。

使用 toJSON() 可以得到适合日志记录的对象:

// 示例:HTTP 200,但接口返回业务失败。
// 以下为 error.toJSON() 的结果:
{
  "code": "BusinessError",
  "status": 200,
  "message": "PagesProjectNotExist (log_id: abc123)"
}

错误码

DataError.code 是 SDK 的错误分类,不等同于数据服务的业务错误码。程序应判断 code,不解析 message,也不要直接把上游 status 用作应用接口的响应状态。

code常见场景
BadRequest参数格式不正确、数据无法 JSON 序列化(如循环引用或 BigInt),或 HTTP 400
UnauthorizedHTTP 401,未认证
ForbiddenHTTP 403,无访问权限
NotFoundHTTP 404
ConflictHTTP 409
PayloadTooLargeHTTP 413,请求体过大
TooManyRequestsHTTP 429,请求频率受限
TimeoutErrorHTTP 408/504,或 fetch 抛出 TimeoutError
AbortErrorfetch 抛出 AbortError,请求被取消
ConfigurationError缺少 IGA Pages 服务端运行时所需的 IGA_AUTH_KEY
ConnectionErrorfetch 连接失败等异常
ServiceUnavailableHTTP 5xx(504 除外)
BusinessErrorHTTP 成功,但响应中的业务 code 非零
InvalidResponse响应 JSON 解析失败、响应信封或成功数据结构不符合契约
UnknownError其他未映射的 HTTP 错误状态

当前,鉴权失败(如 Page SDK Token 无效)时,数据服务返回 HTTP 400,SDK 抛出 BadRequest。

记录错误日志

message 保留上游错误原因,并在有 log_id 时附带日志 ID。SDK 会对 message 中的凭证脱敏,并将长度限制在 2048 个字符以内。

使用 toJSON() 把错误信息记录在服务端日志中,供问题排查使用。向浏览器返回应用自己的错误提示。

重试写操作

出现 TimeoutError、ConnectionError 或 ServiceUnavailable 时,写操作可能已经在数据服务中完成。重试前按操作类型处理:

  • create:直接重试可能产生重复记录。重试前,用 list 按业务字段查询,确认记录是否已经创建。
  • remove:首次请求已删除记录时,重试会抛出 BusinessError。

完整示例

以下示例是一个 Next.js Route Handler。GET 从请求参数 page 读取页码,返回当前页记录和记录总数;POST 创建记录后读取完整记录并返回:

import {
  create,
  get,
  list,
  update,
  remove,
} from "@iga-pages/data";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const pageNumber = Math.max(
    1,
    Number.parseInt(searchParams.get("page") ?? "", 10) || 1,
  );

  const result = await list({
    filter: { kind: "todo" },
    pageNumber,
    pageSize: 100,
  });

  return Response.json({
    items: result.Items,
    page: result.PageNumber,
    total: result.TotalCount,
  });
}

export async function POST(request: Request) {
  const input = await request.json();
  const { ID } = await create({
    kind: "todo",
    title: input.title,
    completed: false,
  });

  const record = await get(ID);
  return Response.json({ record }, { status: 201 });
}

更新和删除记录时,分别调用:

await update(ID, { completed: true });
await remove(ID);
最近更新时间:2026.11.06 11:46:31
这个页面对您有帮助吗?
有用
有用
无用
无用