关于 building-nango-functions-remotely
此技能指导开发者在无需检出 Nango 项目的情况下,构建和修改 Nango action 与 sync。它强调同步检查点、删除策略以及通过 Nango API 编译、试运行和部署。
主要功能
- 01远程编译、试运行与部署函数
- 02构建 Nango action 和 sync
- 03设计检查点与删除策略
- 04遵循输入、架构与运行时约束
适用场景
- 01为 Nango 集成实现一次性 action
- 02设计可恢复的分页或增量 sync
- 03通过 API 验证并部署单文件函数
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ nangohq/ skills --skill building-nango-functions-remotely安装后在 Claude Code 中输入/building-nango-functions-remotely确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/actions.mdapi.mdsyncs.md详细介绍
远程构建 Nango 函数
无需检出 Nango 项目,即可通过向 Nango 的 /functions/compile、/functions/dryruns 和 /functions/deployments API 发送单文件 TypeScript 函数,构建 Nango 操作和同步。
实施范围
- 构建或修改 Nango 函数实现
- 使用
createAction()在 Nango 中构建操作 - 使用
createSync()在 Nango 中构建同步 - 使用当前工作流技能处理编译、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
同步:
- 定期运行或由 webhook 驱动的缓存更新,使用
createSync()构建 - 先完成同步策略门槛
- 编写代码前阅读
references/syncs.md
必需输入(缺少时询问用户)
始终需要:
- 集成 ID(提供商名称)
- 脚本/函数名称(kebab-case)
- API 参考 URL 或示例响应
- 如果当前工作流将验证或 dryrun 函数,则需要连接 ID
操作专属:
- 用例摘要
- 输入参数
- 输出字段
- 如果需要,提供 Metadata JSON
- 如果当前工作流将验证或 dryrun 操作,则提供测试输入 JSON(无输入操作使用
{})
同步专属:
- 模型名称(单数、PascalCase)
- 频率(每小时、每 5 分钟等)
- 检查点架构(时间戳、游标、页面令牌、偏移量/页码、
since_id或组合值) - 检查点如何更改提供商请求或恢复状态
- 删除策略(删除记录端点/webhook,或说明为何必须全量刷新)
- 如果提议全量刷新,需提供文档/示例响应中阻碍使用检查点的确切提供商限制
- 如果需要,提供 Metadata 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();否则依靠 Schema 和所选的验证工作流。
Schema 语义
- 非必填输入默认使用
.optional()。 - 仅在
null有意义时使用.nullable(),通常用于清除更新;如果调用方也可以省略该字段,则同时添加.optional()。 - 原始提供方 Schema 应与提供方保持一致:省略字段时使用
.optional(),显式为null时使用.nullable(),仅当提供方确实同时采用这两种方式时才使用.nullish()。 - 最终操作输出和规范化同步模型应优先使用
.optional();除非null有意义,否则应将上游的null规范化为省略。 - 生成的 Schema 默认应将非必填输入和规范化输出设为
.optional();只有上游契约有充分依据时才放宽限制。 - 优先使用
.nullable(),而不是z.union([z.null(), T])或z.union([T, z.null()])。 - 仅当输出 Schema 允许时才返回
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 文档(快速链接)
- Functions 运行时 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 文档无法正常显示时
如果网页抓取返回的文档不完整(由 JavaScript 渲染):
- 向用户索取一个示例响应
- 如果工作区中已有 Nango 操作或同步,请参照其模式
- 使用该技能专用的验证或 dryrun 工作流,直到通过为止
前置条件(编写代码前完成)
- 无需检出 Nango 项目。
- 按以下顺序解析
NANGO_SERVER_URL:环境变量、.env文件,然后回退到https://api.nango.dev。 - 调用函数端点前解析
NANGO_SECRET_KEY。 - 使用与该密钥绑定的环境。
- 确认密钥具有所需的作用域:编译需要
environment:functions:compile,dryrun 和轮询需要environment:functions:dryrun,部署需要environment:deploy。 - 除非有直接证据表明远程端点接受多文件负载,否则将函数放在一个独立的 TypeScript 文件中。
- 不要在当前项目/目录中创建或修改任何文件。如果需要创建文件,请使用临时文件夹。
工作流(必需)
- 判断这是一个操作还是同步。
- 阅读对应的参考文件:
references/actions.md或references/syncs.md。 - 对于同步,检查提供方文档或载荷中的检查点和删除信息,判断端点返回的是完整数据还是变更行,并完成 Sync Strategy Gate。
- 收集必需的输入和外部值,包括目标环境的
NANGO_SECRET_KEY,以及 dryrun 所需的任何元数据。 - 依次从环境变量、
.env文件和https://api.nango.dev获取NANGO_SERVER_URL,确定主机地址。 - 使用
createAction()或createSync(),将函数编写或更新为一个独立的 TypeScript 文件。 - 使用
POST {host}/functions/compile进行编译,直到编译通过。 - 使用
POST {host}/functions/dryruns启动 dryrun,并提供目标集成、连接、函数类型、代码,以及所需的任何input、metadata或checkpoint。 - 轮询
GET {host}/functions/dryruns/{id},直到 dryrun 状态变为success或failed。 - 如果编译或 dryrun 无法通过,则停止并报告缺少的外部状态、输入或 API 契约不匹配问题。
- 仅在收到请求时,使用
POST {host}/functions/deployments启动部署。 - 轮询
GET {host}/functions/deployments/{id},直到部署状态变为success或failed。
远程 API 工作流(必需)
进行远程调用前,请先阅读 references/api.md。
必需顺序:
- 首先使用
POST /functions/compile进行编译。 - 其次使用
POST /functions/dryruns启动 dryrun。 - 使用
GET /functions/dryruns/{id}轮询 dryrun 状态,直到进入终态。 - 仅在收到请求时,使用
POST /functions/deployments启动部署。 - 使用
GET /functions/deployments/{id}轮询部署状态,直到进入终态。
规则:
- 这些端点均为相对路径。始终根据选定的
NANGO_SERVER_URL解析它们。 - 发送
Authorization: Bearer <NANGO_SECRET_KEY>和Content-Type: application/json。 - API 密钥所需的权限范围为:编译需要
environment:functions:compile,创建 dryrun 和查询状态需要environment:functions:dryrun,创建部署和查询状态需要environment:deploy。 - 除非 API 文档或现有调用方证明支持,否则不要发送查询参数。
- 根据服务器返回的验证错误修正载荷。如果 API 拒绝请求,不要臆造未记录的字段。
- 编译请求仅发送
{ "code": "..." }。 - Dryrun 请求发送
integration_id、function_type、code和connection_id。 - 部署请求发送
type: "function"、integration_id、function_name、function_type和code。仅在明确需要时添加version或allow_destructive。 - 对于操作,仅在需要时于 dryrun 中包含
input和metadata。 - 对于同步,仅在需要模拟恢复运行时,于 dryrun 中包含
metadata和checkpoint。新的同步设计中不要加入last_sync_date。 - Dryrun 为异步操作。
POST /functions/dryruns会返回一个id;轮询GET /functions/dryruns/{id},获取status、output、result或error。不要调用/functions/dryruns/{id}/result;该端点仅供沙箱内部使用。 - 部署为异步操作。
POST /functions/deployments会返回一个id;轮询GET /functions/deployments/{id},获取status、deployed、deployed_functions、output或error。不要调用/functions/deployments/{id}/result;该端点仅供沙箱内部使用。 - 将
waiting和running视为非终态;仅在状态变为success或failed时停止轮询。 - 远程 dryrun 不支持 CLI 的
--validate或--save;它会在运行前编译,并通过状态端点返回执行结果,但不会记录本地模拟数据。
最终检查清单
操作:
- 使用了
references/actions.md中的操作模式 - Schema 和类型清晰,且函数在一个文件中保持自包含
- 在需要时,
createAction()包含输入、输出和 scopes;省略已弃用的endpoint - 字段采用透传大小写格式,或遵循 API 中占多数的大小写格式
- Provider 调用包含 API 文档链接注释和有意设置的重试
- 对预期失败使用
nango.ActionError - 主机地址通过
NANGO_SERVER_URL、.env或https://api.nango.dev确定 - 使用
POST /functions/compile编译成功 - 使用
POST /functions/dryruns启动 Dryrun - 通过
GET /functions/dryruns/{id}查询,Dryrun 状态达到success,且输出符合预期 - 在有要求时,使用
POST /functions/deployments启动部署 - 在有要求时,通过
GET /functions/deployments/{id}查询,部署状态达到success
同步:
- 使用了
references/syncs.md中的同步模式 - 已定义 Models 映射,ID 为稳定的字符串;除非
null有意义,否则规范化模型优先使用.optional() - 优先选择增量同步;除非文档或数据载荷明确证明有必要进行全量刷新,否则应提供检查点 Schema
- 在开始时读取
nango.getCheckpoint(),并在每页或每批处理后运行nango.saveCheckpoint() - 检查点数据会改变 Provider 请求或恢复状态(
since、updated_after、cursor、page_token、offset、page、since_id等) - 仅同步变更数据的检查点同步(
modified_after、updated_after、变更记录端点)不使用trackDeletesStart()/trackDeletesEnd() - 如果未使用检查点,响应会准确说明为何不存在可行的检查点策略
- 全量刷新同步具有
checkpointSchema,会从中恢复分页,并在每页后调用saveCheckpoint(),以免执行窗口超时后从第 1 页重新开始 - 仅在调用
clearCheckpoint()后,全量刷新中的trackDeletesEnd()才会运行,且只在完成最后一页的那次运行中执行 - Provider API 调用使用
retries: 3;没有任何同步重试值超过3,除非有记录在案的例外 - 除非远程 API 证明支持多文件,否则函数应在一个文件中保持自包含
- 主机地址通过
NANGO_SERVER_URL、.env或https://api.nango.dev确定 - 使用
POST /functions/compile编译成功 - 使用
POST /functions/dryruns启动 Dryrun - 通过
GET /functions/dryruns/{id}查询,Dryrun 状态达到success,并返回符合预期的变更集 - 在有要求时,使用
POST /functions/deployments启动部署 - 在有要求时,通过
GET /functions/deployments/{id}查询,部署状态达到success
评论
支持 Markdown