关于 building-nango-functions-locally
用于在已检出的 Nango 项目中构建 action 和 sync 的技能。它要求为 sync 设计适配服务商的检查点与删除策略,并使用项目本地的验证、测试和部署流程。
主要功能
- 01使用 createAction() 构建 action
- 02使用 createSync() 构建 sync
- 03规划检查点和删除处理
- 04验证服务商输入与数据模式
- 05使用本地编译、试运行、测试和部署流程
适用场景
- 01实现由用户触发的服务商 API action
- 02构建定时或 webhook 驱动的数据同步
- 03通过检查点恢复分页同步
- 04在本地验证并测试 Nango 函数
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ nangohq/ skills --skill building-nango-functions-locally安装后在 Claude Code 中输入/building-nango-functions-locally确认已加载。
附带文件references/actions.mdreferences/syncs.md
详细介绍
在本地构建 Nango 函数
在已检出的 Nango 项目中,使用本地 CLI 验证和测试工作流,构建可部署的 Nango 操作和同步。
实现范围
- 构建或修改 Nango 函数实现
- 在 Nango 中使用
createAction()构建操作 - 在 Nango 中使用
createSync()构建同步 - 使用当前工作流技能处理编译、dryrun、测试和部署机制
同步策略关卡(写代码前必须完成)
如果任务涉及同步,请在写代码前阅读 references/syncs.md,并首先说明以下其中一条路径:
- 检查点方案:
- 更改来源(
updated_at、modified_since、变更记录端点、游标、页令牌、偏移量/页码、since_id或 webhook) - 检查点架构
- 检查点如何改变提供方请求或恢复状态
- 请求仍会遍历完整数据集,还是只返回变更的行
- 删除策略
- 更改来源(
- 全量刷新阻碍因素:
- 文档或示例载荷中的具体提供方限制
- 检查点为何无法在此处发挥作用
无效的同步实现:
- 因为更简单而采用全量刷新
- 使用
saveCheckpoint()却不使用getCheckpoint() - 读取或保存检查点,却不在请求参数或分页状态中使用它
- 在新同步中使用
syncType: 'incremental'或nango.lastSyncDate - 全量刷新没有
checkpoint架构,或未在每一页后保存检查点——如果运行超出执行时间窗口,就会从第 1 页重新开始 - 将
saveCheckpoint()放在仅在还有更多页面时才执行的条件中(例如if (nextCursor) { await nango.saveCheckpoint(...) })。每个成功页面都必须调用saveCheckpoint(),包括最后一页;否则,单页同步会在没有检查点记录的情况下执行clearCheckpoint(),并失败并报错checkpoint_conflict。 - 在
clearCheckpoint()之前调用trackDeletesEnd(),或完全没有先调用clearCheckpoint() - 对仅返回变更内容的检查点(
modified_after、updated_after、变更记录端点)使用trackDeletesStart()/trackDeletesEnd()。这些请求会省略未变更的行,因此trackDeletesEnd()会误删这些记录。 - 在已有明确删除记录事件的增量同步中使用
trackDeletesStart()/trackDeletesEnd()
选择路径
操作:
- 使用
createAction()构建的一次性、由用户触发的请求 - 写代码前阅读
references/actions.md
同步:
- 使用
createSync()构建的定时或 webhook 驱动的缓存更新 - 首先完成同步策略关卡
- 写代码前阅读
references/syncs.md
必需输入(缺少时询问用户)
始终需要:
- 集成 ID(提供方名称)
- 脚本/函数名称(kebab-case)
- API 参考 URL 或示例响应
- 如果当前工作流将验证或 dryrun 该函数,则需要连接 ID
操作专属:
- 用例摘要
- 输入参数
- 输出字段
- 如有需要,提供元数据 JSON
- 如果当前工作流将验证或 dryrun 该操作,则需要测试输入 JSON(无输入操作请使用
{})
同步专属:
- 模型名称(单数、PascalCase)
- 频率(每小时、每 5 分钟等)
- 检查点架构(时间戳、游标、页令牌、偏移量/页码、
since_id或复合类型) - 检查点如何改变提供方请求或恢复状态
- 删除策略(已删除记录端点/webhook,或说明为何必须全量刷新)
- 如果建议全量刷新,需提供文档/示例响应中阻碍使用检查点的具体提供方限制
- 如有需要,提供元数据 JSON(team_id、workspace_id)
如果缺少任何必需的外部值,请先检查仓库和提供方文档,然后提出有针对性的问题。对于同步,只要提供方支持,就选择检查点和删除策略。如果找不到可行的检查点策略,请在编写全量刷新代码前准确说明原因。
不可妥协的规则
共享平台约束
- Nango 函数使用
createAction()/createSync()。 - 不能添加任意软件包。仅当所选工作流支持时,才使用相对导入;内置模块包括
zod、crypto/node:crypto和url/node:url。 - 在函数代码之外,使用 Nango HTTP API 查询连接、获取凭据和调用代理。不要臆造 CLI 令牌或连接命令。
- 在每个服务提供商调用上方添加 API 文档链接注释。
- Action 输出不得超过 2MB。
- 文件上传和下载无法通过 action 实现(沙盒运行时:没有
fs、没有axios,输出上限为 2MB)。请改用{integration}/proxy/中的代理脚本,并使用@nangohq/node——参见references/actions.md。 - HTTP 重试默认值为
0;请有意设置retries。通常最多设为3;对于 sync 的服务提供商调用,除非文档证明更高值安全且必要,否则实际上禁止设置超过3。除非 API 支持幂等性,否则避免对非幂等写入进行重试。 - 不要设置已弃用的函数定义路由字段:action 的
endpoint和 sync 的endpoints。通过 SDK/API 按 action 名称触发 action,并通过 records API 获取 sync 记录。
Sync 规则
- Sync 记录需要稳定的字符串
id。 - 新建 sync 时应定义
checkpoint架构,先调用nango.getCheckpoint(),并在每个页面或批次处理后调用nango.saveCheckpoint()。 - 只有在 checkpoint 会改变请求或恢复状态(
since、updated_after、cursor、page_token、offset、page、since_id等)时,它才有效。不使用 checkpoint 却保存 checkpoint,不算增量同步。 - 新建 sync 不得使用
syncType: 'incremental'或nango.lastSyncDate。 - 默认使用
nango.paginate(...)+nango.batchSave(...)。当cursor、link或offset分页适用时,避免手动编写while (true)循环。 - 服务提供商返回删除记录、墓碑记录或删除 webhook 时,优先使用
batchDelete()。 - 只有在服务提供商无法返回变更、删除或恢复状态,或者数据集很小时,才使用全量刷新。
- 使用全量刷新时,请引用文档或负载中的具体服务提供商限制。“这样更简单”并不足以作为理由。
- 全量刷新 sync 也需要定义
checkpoint架构(涵盖页面/游标/偏移量),以记录分页进度,而不仅仅是增量 sync 需要定义。Nango sync 在有时间限制的执行窗口内运行;如果全量刷新没有 checkpoint,那么每次运行超出执行窗口时都会从第 1 页重新开始,浪费计算资源重复获取相同的前几页,并且永远无法到达其余页面。 deleteRecordsFromPreviousExecutions()已弃用。全量刷新时,每次执行都要调用trackDeletesStart()(安全且幂等——不会覆盖已打开窗口的开始位置),然后在每页处理后调用saveCheckpoint(),最后一页处理完后调用clearCheckpoint(),并且仅在clearCheckpoint()之后调用trackDeletesEnd()。- 全量刷新中,每次成功处理页面后都要调用
saveCheckpoint(),包括最后一页,并且必须在调用clearCheckpoint()之前执行。绝不要以“还有更多页面”为条件来决定是否保存。如果某条独立执行路径完全没有创建 checkpoint(例如没有处理任何页面),则不要在该路径调用clearCheckpoint();这会在运行时抛出checkpoint_conflict。此例外不能代替保存终止页面的 checkpoint。 - 绝不要将
trackDeletesStart()/trackDeletesEnd()与仅包含变更数据的 checkpoint(modified_after、updated_after、仅返回变更记录的端点等)结合使用。它们会省略未变更的行,因此trackDeletesEnd()会将这些行删除。 - 使用 checkpoint 的全量刷新仍然是全量刷新。仅在完成并清除 checkpoint 的那次运行中调用
trackDeletesEnd()。 - 如果 sync 需要元数据(例如
team_id、workspace_id、guild_id),请设置autoStart: false。调用方设置元数据前,sync 无法运行,因此自动启动会导致失败。
约定
- 字段大小写应与外部 API 保持一致。透传字段保留服务提供方的大小写;非透传字段使用该 API 中占多数的大小写形式。
- 优先使用明确的字段名。
- 为 ID、时间戳、枚举值和 URL 添加
.describe()示例。 - 避免使用
any;改用内联映射类型。 - 列表操作应提供
cursor,以及采用该 API 中占多数的大小写形式的下一游标字段(next_cursor、nextCursor等)。 - 仅在需要自定义验证或日志记录时使用
nango.zodValidateInput();否则,依赖架构和所选的验证工作流。
架构语义
- 默认将非必填输入设为
.optional()。 - 仅在
null有实际含义时使用.nullable(),通常用于清除更新;如果调用方也可以省略该字段,则同时添加.optional()。 - 原始服务提供方架构应与服务提供方保持一致:对于省略的字段使用
.optional(),对于显式的null使用.nullable(),只有当服务提供方确实两种情况都有时才使用.nullish()。 - 最终操作输出和规范化后的同步模型应优先使用
.optional();除非null有实际意义,否则将上游的null规范化为省略。 - 对于非必填输入和规范化输出,生成的架构默认使用
.optional();只有上游契约有充分依据时才放宽限制。 - 优先使用
.nullable(),而不是z.union([z.null(), T])或z.union([T, z.null()])。 - 仅当输出架构允许时才返回
null。 z.object()默认会剥离未知键。对于服务提供方透传,可使用z.object({}).passthrough()、z.record(z.unknown())或仅带最少精化的z.unknown()。
字段命名和大小写规则
- 使用符合 API 中占多数的大小写形式的明确后缀:ID(
user_id、userId)、名称(channel_name、channelName)、电子邮件(user_email、userEmail)、URL(callback_url、callbackUrl)和时间戳(created_at、createdAt)。
映射示例(API 要求使用不同的参数名):
const InputSchema = z.object({
userId: z.string()
});
const config: ProxyConfiguration = {
endpoint: 'users.info',
params: {
user: input.userId
},
retries: 3
};
如果 API 使用 snake_case,则改用 user_id。目标是与 API 保持一致。
参考资料
- 操作模式、CRUD 示例、元数据用法和 ActionError 示例:
references/actions.md - 同步模式、具体检查点示例、删除策略和完整刷新回退方案:
references/syncs.md
有用的 Nango 文档(快速链接)
- 函数运行时 SDK 参考:https://nango.dev/docs/reference/functions
- 实现操作:https://nango.dev/docs/implementation-guides/use-cases/actions/implement-an-action
- 实现同步:https://nango.dev/docs/implementation-guides/use-cases/syncs/implement-a-sync
- 检查点:https://nango.dev/docs/implementation-guides/use-cases/syncs/checkpoints
- 删除检测(完整与增量):https://nango.dev/docs/implementation-guides/use-cases/syncs/deletion-detection
- 测试集成(dryrun、
--save、Vitest):https://nango.dev/docs/implementation-guides/platform/functions/testing - Nango HTTP API 参考:https://nango.dev/docs/reference/api
API 文档无法呈现时
如果网页抓取返回的文档不完整(由 JS 渲染):
- 请用户提供一个示例响应
- 如果工作区中已有 Nango 操作或同步,请参考其模式
- 使用技能专属的验证或 dryrun 工作流,直至通过
工作流(必需)
- 判断这是一个操作还是一次同步。
- 阅读对应的参考文件:
references/actions.md或references/syncs.md。 - 对于同步,检查提供方文档或有效负载中的检查点和删除信息,判断端点返回的是完整数据还是变更的行,并完成 Sync Strategy Gate。
- 收集必需的输入和外部值。查询连接、凭据或发现信息时,使用 Nango HTTP API。
- 确认这是一个 Zero YAML TypeScript 项目(没有
nango.yaml),且当前位于 Nango 根目录(存在.nango/)。 - 在
{integrationId}/actions/或{integrationId}/syncs/下创建或更新函数,应用共享的架构和大小写规则,然后在index.ts中注册。 - 使用
nango dryrun ... --validate -e dev --no-interactive --auto-confirm进行验证。 - 如果验证无法通过,请停止并报告缺少的外部状态或输入。
- 验证通过后,运行
nango dryrun ... --save,然后运行nango generate:tests,再运行npm test。 - 仅在用户要求时,使用
nango deploy dev部署。
前置条件(编写代码前执行)
确认 TypeScript 项目(没有 nango.yaml)
此技能仅支持使用 createAction() / createSync() 的 TypeScript 项目。
ls nango.yaml 2>/dev/null && echo "YAML PROJECT DETECTED" || echo "OK - No nango.yaml"
如果看到 YAML PROJECT DETECTED:
- 立即停止。
- 告知用户先升级到 TypeScript 格式。
- 不要尝试混用 YAML 和 TypeScript。
参考:https://nango.dev/docs/implementation-guides/platform/migrations/migrate-to-zero-yaml
验证 Nango 项目根目录
确认 Nango 根目录前,不要创建文件:
ls -la .nango/ 2>/dev/null && pwd && echo "IN NANGO PROJECT ROOT" || echo "NOT in Nango root"
如果看到 NOT in Nango root:
cd到包含.nango/的目录- 重新运行检查
- 不要使用绝对路径来规避问题
所有文件路径都必须相对于 Nango 根目录。在 Nango 根目录中创建文件时,如果额外添加路径前缀,会创建嵌套目录并导致构建失败。
项目结构和命名
./
|-- .nango/
|-- index.ts
|-- hubspot/
| |-- actions/
| | `-- create-contact.ts
| `-- syncs/
| `-- fetch-contacts.ts
`-- slack/
`-- actions/
`-- post-message.ts
- 提供方目录:小写(
hubspot、slack) - 操作文件:kebab-case(
create-contact.ts) - 同步文件:kebab-case(许多团队使用
fetch-前缀,但它是可选的) - 每个文件只包含一个函数
- 所有操作和同步都必须在
index.ts中导入
在 index.ts 中注册脚本(必需)
仅使用副作用导入。包含 .js 扩展名。
// index.ts
import './github/actions/get-top-contributor.js';
import './github/syncs/fetch-issues.js';
注册不正确的表现:文件可以编译,但会看到 No entry points found in index.ts...,或者函数始终不会出现。
Dryrun、模拟和测试(必需)
必需的循环:
- 反复运行
nango dryrun ... --validate -e dev --no-interactive --auto-confirm,直到验证通过。 - 操作:始终传入
--input '{...}'(无输入操作使用--input '{}')。 - 同步:需要模拟恢复运行时,使用
--checkpoint '{...}'。 - 如果验证无法通过,请停止并说明所需但缺少的外部状态或输入。
- 验证通过后,运行
nango dryrun ... --save -e dev --no-interactive --auto-confirm,生成<script-name>.test.json。 - 运行
nango generate:tests,然后运行npm test。
示例:
# Validate an action
nango dryrun <action-name> <connection-id> --validate -e dev --no-interactive --auto-confirm --input '{"key":"value"}'
# Validate a no-input action
nango dryrun <action-name> <connection-id> --validate -e dev --no-interactive --auto-confirm --input '{}'
# Validate a sync
nango dryrun <sync-name> <connection-id> --validate -e dev --no-interactive --auto-confirm
# Validate a resumed sync with a checkpoint
nango dryrun <sync-name> <connection-id> --validate -e dev --no-interactive --auto-confirm --checkpoint '{"updated_after":"2024-01-15T00:00:00Z"}'
# Record action mocks after validation passes
nango dryrun <action-name> <connection-id> --save -e dev --no-interactive --auto-confirm --input '{"key":"value"}'
# Record sync mocks after validation passes
nango dryrun <sync-name> <connection-id> --save -e dev --no-interactive --auto-confirm
# Stub metadata when needed
nango dryrun <script-name> <connection-id> --validate -e dev --no-interactive --auto-confirm --metadata '{"team_id":"123"}'
硬性规则:
- 将
<script-name>.test.json视为生成的输出。绝不要创建、编辑、重命名或移动它。 - 如果模拟数据有误或已过时,请修正代码并使用
--save重新录制。 - 不要在
*.test.json中硬编码错误有效负载;对于 404、401、429 或超时情况,请使用带vi.spyOn(...)的 Vitest 测试。 - 连接 ID 是第二个位置参数;不要使用
--connection-id。 - 当多个集成中的脚本名称重叠时,使用
--integration-id <integration-id>。 - 对于新的增量同步,优先使用
--checkpoint;--lastSyncDate是旧模式。 - 如果
nango不在PATH中,请使用npx nango ...。 - CLI 升级提示可能会阻塞自动化;必要时设置
NANGO_CLI_UPGRADE_MODE=ignore。
参考:https://nango.dev/docs/implementation-guides/platform/functions/testing
部署(可选)
将函数部署到 Nango 账户中的某个环境:
nango deploy dev
# Deploy only one function
nango deploy --action <action-name> dev
nango deploy --sync <sync-name> dev
参考:https://nango.dev/docs/implementation-guides/use-cases/actions/implement-an-action
最终检查清单
操作:
- 已验证 Nango 根目录
- 已参考
references/actions.md中的操作模式 - 架构和类型清晰,缺失值规则符合服务商与标准化契约的要求
- 在必要时,
createAction()包含输入、输出和作用域;省略已弃用的endpoint - 字段采用透传大小写形式或 API 的主流大小写形式
- 服务商调用包含 API 文档链接注释,并设置了有意为之的重试
- 预期的失败情况使用
nango.ActionError - 已在
index.ts中注册 - 使用
--validate -e dev --no-interactive --auto-confirm --input '{...}'运行 Dryrun 成功 - 在
--validate之后,通过nango dryrun ... --save生成了<action-name>.test.json - 已运行
nango generate:tests,且npm test通过
同步:
- 已验证 Nango 根目录
- 已参考
references/syncs.md中的同步模式 - 已定义模型映射表,ID 为稳定字符串;除非
null值有特殊意义,否则标准化模型优先使用.optional() - 优先选择增量同步,并设置检查点架构;只有文档或负载明确说明合理时,才采用全量刷新
- 在开头读取
nango.getCheckpoint(),并在每页或每批次后调用nango.saveCheckpoint() - 检查点数据会改变服务商请求或恢复状态(
since、updated_after、cursor、page_token、offset、page、since_id等) - 仅同步变更数据的检查点同步(
modified_after、updated_after、变更记录端点)不使用trackDeletesStart()/trackDeletesEnd() - 如果未使用检查点,响应会准确说明为什么不存在可行的检查点策略
- 原始服务商架构正确区分省略值与
null,字段采用透传大小写形式或 API 的主流大小写形式 - 除非 API 确实无法使用 Nango 的分页器,否则使用
nango.paginate() - 服务商 API 调用使用
retries: 3;除非有记录在案的例外,否则同步重试值不超过3 - 删除策略与同步类型匹配:仅当服务商返回明确的删除记录时,增量同步才使用
batchDelete();否则,全量刷新回退方案会在获取/保存数据前调用trackDeletesStart(),并且仅在成功完成全量获取和保存后调用trackDeletesEnd() - 全量刷新同步具有
checkpoint架构,会从中恢复分页,并在每页后调用saveCheckpoint(),以免执行时限超时后从第 1 页重新开始 - 仅在调用
clearCheckpoint()后,且在完成最后一页的运行中,才执行全量刷新的trackDeletesEnd() - 如有需要,已处理元数据
- 已在
index.ts中注册 - 使用
--validate -e dev --no-interactive --auto-confirm运行 Dryrun 成功 - 在
--validate之后,通过nango dryrun ... --save生成了<sync-name>.test.json - 已运行
nango generate:tests,且npm test通过