首页
/ Insomnia 的 insomnia-api 包:Insomnia Cloud 共享 API 客户端的设计与使用

Insomnia 的 insomnia-api 包:Insomnia Cloud 共享 API 客户端的设计与使用

2026-09-05 09:08:19作者:鲍丁臣Ursa

insomnia-api 是 Insomnia 仓库中负责与 Insomnia Cloud 后端通信的共享 API 层,桌面应用(insomnia)与 inso CLI 都通过它调用账户、组织、协作、Mock 等云端功能。本文以 packages/insomnia-api/README.md 为骨架,结合包内源码,完整讲解这个包如何安装引入、如何通过 configureFetch 注入可替换的网络实现、11 个功能模块分别封装了哪些端点,以及调用方在路由层如何利用 isApiError 处理服务端错误。读完本文,你可以准确理解该包的依赖注入架构、各模块的 API 面(方法、路径、请求体),并在自己的代码中正确引用与排错。

包的定位:跨 insomnia 与 inso-cli 共享的 API 功能与类型

README 对包的定位一句话概括得非常清楚:

This package contains the API functionality and types for the Insomnia application which could be shared across insomnia and inso-cli.

也就是说,这个包同时承担两个职责:

  1. API 功能封装:把每个云端端点封装成语义化的 TypeScript 函数(如 getUserProfilecreateTeamProject),调用方不需要关心 URL 拼接、请求头与错误解析;
  2. 类型共享UserUserEncryptionKeysSpaceCurrentPlan 等类型从本包导出,让桌面端 UI、主进程与 CLI 工具使用同一份类型定义。

package.json 可以看到关键元信息:

  • 包名 insomnia-apiprivate: true(不发布到 npm,只在 monorepo 内部通过 workspace 消费);
  • 当前版本 13.2.0
  • exports 字段直接把入口指到 TypeScript 源码 ./src/index.tsimporttypes 条件均指向同一文件),因此消费方无需构建步骤,直接以源码形式引入;
  • 运行时依赖只有一个:@getinsomnia/insomnia-v3-fetch(OpenAPI 生成的 v3 SDK),用于部分 v3 端点(如 spaces.tsuser.ts 中的类型)。

包提供的脚本(lint / type-check / test)也表明它以独立的 TypeScript 子包形式接受检查与单测:

"scripts": {
  "lint": "eslint . --ext .ts,.tsx --cache",
  "type-check": "tsc --noEmit --project tsconfig.json",
  "test": "vitest run"
}

安装与引入:npm workspace 零安装 + 统一入口导出

README 的「Install」一节指出该包走 npm workspace,无需单独安装,在仓库根目录执行一次依赖安装后即可被其他 workspace 包直接 import 'insomnia-api'。README 给出的标准引入方式是:

import { getUserProfile, getEncryptionKeys, type User, type UserEncryptionKeys } from 'insomnia-api';

所有导出集中在 src/index.ts,结构上就是 11 个功能模块的桶式再导出,外加 fetch.ts 中 4 个与网络层相关的对象:

export * from './user';
export * from './vault';
export * from './enterprise';
export * from './trial';
export * from './project';
export * from './collaborators';
export * from './invite';
export * from './organizations';
export * from './spaces';
export * from './mock';
export * from './vcs';

export { configureFetch, type FetchConfig, ResponseFailError, isApiError } from './fetch';

值得注意的是 user.tsspaces.ts 等文件还会把 @getinsomnia/insomnia-v3-fetch SDK 的部分类型(如 UserUserEncryptionKeysSpace)二次导出,使消费方只需依赖 insomnia-api 一个包名即可拿到全部云端类型——这正是 README 所说的 "API functionality and types" 的落地方式。

网络层设计:可注入的 fetch 与统一错误类型

理解这个包的关键在于 src/fetch.ts。它并不直接发起 HTTP 请求,而是定义了一个可注入的请求抽象

export interface FetchConfig {
  method: 'POST' | 'PUT' | 'GET' | 'DELETE' | 'PATCH';
  path: string;
  sessionId: string | null;
  organizationId?: string | null;
  data?: unknown;
  origin?: string;
  headers?: Record<string, string>;
  timeout?: number;
}

export type Fetch = <T = void>(options: FetchConfig) => Promise<T>;

FetchConfig 各字段的含义:

字段 必填 说明
method 仅允许 5 种 REST 动词
path 相对 API 根地址的路径,如 /v3/users/me
sessionId 会话凭据,最终变成请求头;传 null 表示匿名请求
organizationId 团队上下文,映射为 X-Insomnia-Org-Id
data JSON 请求体
origin 覆盖默认 API 根地址(用于 mockbin、外部资产等非同源端点)
headers 附加请求头
timeout 超时时间,由具体实现决定默认值

注入机制采用「一次性配置」的守卫模式:

let fetch: Fetch = <T = void>(_options: FetchConfig): Promise<T> => {
  throw new Error('Fetch has not been configured. Please call configureFetch() at application startup.');
};

export function configureFetch(_fetch: Fetch) {
  if (configured) {
    throw new Error('Fetch has already been configured and cannot be re-configured.');
  }
  fetch = _fetch;
  configured = true;
}

这样设计有两个直接收益:

  • 默认抛错:任何忘记初始化就调用 API 的代码会立即得到明确的启动期错误,而不是静默失败;
  • 环境无关:渲染进程、主进程、CLI 各自注入符合自己运行环境的 fetch 实现。

桌面端如何完成注入

在桌面应用中,两个入口分别在启动时完成注入。渲染进程 entry.client.tsx 与主进程 entry.main.ts 都执行同样的模式:

import { configureFetch } from 'insomnia-api';

configureFetch(options => insomniaFetch({ ...options, onDeepLink: (uri: string) => window.main.openDeepLink(uri) }));

真正干活的实现是 insomnia-fetch.ts,它把 FetchConfig 翻译成一个完整的 fetch 调用,并附加以下请求头:

请求头 来源 用途
X-Insomnia-Client getClientString() 客户端标识(版本、构建号)
insomnia-request-id generateId('desk') 请求追踪 ID
X-Origin origingetApiBaseURL() 声明请求来源,后端据此选择端点基址
X-Session-Id sessionId 会话认证
Content-Type: application/json data 时自动附加 JSON 序列化
X-Insomnia-Org-Id organizationId 团队作用域
X-Mockbin-Test Playwright 测试环境自动附加 让 mock 服务识别测试流量

除请求头外,该实现还处理了三类横切逻辑:

  1. 超时AbortSignal.timeout(timeout) 包裹请求,超时抛出 insomniaFetch timed out: {method} {path}
  2. 深链:若响应头带 x-insomnia-command,回调 onDeepLink(uri) 打开 deep link(这也是 entry.client.tsx 注入时传入该回调的原因);
  3. 错误规范化:非 2xx 响应会尝试解析 JSON 中的 error/message 字段,统一抛出 ResponseFailError(携带原始 Response 对象);底层网络错误(如证书、ECONNREFUSED)则从 err.cause 中提取详情后重新抛出。

统一的错误判定

fetch.ts 同时导出了错误类型与类型守卫,调用方无需 instanceof 判断:

export class ResponseFailError extends Error {
  response: Response;
  constructor(name: string, msg: string, response: Response) {
    super(msg);
    this.name = name || 'ResponseFailError';
    this.response = response;
  }
}

export const isApiError = (error: unknown): error is ResponseFailError =>
  error instanceof ResponseFailError;

桌面端路由层大量使用该模式区分「服务端拒绝」与「本地异常」,例如 organization.organizationId.project.organizationId.project.projectId.delete.tsx 中:

import { deleteTeamProject, isApiError } from 'insomnia-api';

// ...
if (isApiError(err)) {
  // 用服务端返回的 name/message 展示错误
}

由于 insomnia-api 本身不关心 Response 如何呈现,这套「抛错类型 + 类型守卫」的组合让 UI 层可以稳定地拿到后端错误码(如 CODE-403)与错误消息。

功能模块总览:11 个领域与对应端点

src/index.ts 再导出的 11 个文件对应 11 个功能领域。以下按模块梳理其封装的端点与方法,全部路径与各函数签名均可在对应源码文件中核对。

user.ts:当前用户、账单与 onboarding

函数 方法 + 路径 说明
logout POST /auth/logout 注销会话
getUserProfile GET /v3/users/me 当前用户资料,返回 User
getEncryptionKeys GET /v3/users/me/encryption-keys 用户加密密钥(SRP 公钥材料)
getOnboardingState GET /v3/users/me/onboarding 新手引导状态
latchRequestThresholdReached POST /v3/users/me/onboarding/request-threshold 幂等闩锁:标记账户达到首个请求毕业阈值,服务端存 sticky bit,重复调用安全,返回 204 无 body
recordUserAction POST /v3/users/me/actions 上报用户行为事件(event_id + action_type
getCurrentPlan GET /v1/billing/current-plan 当前订阅计划,含 typefree / individual / team / enterprise / enterprise-member)、周期、状态(trialing / active)等字段
getUserFiles GET /v1/user/files 列出远端同步文件
getLearningFeature GET /insomnia-production-public-assets/inapp-learning.json 应用内学习入口配置;该端点不在 Insomnia API 域内,origin 指向 Google Storage 且 sessionId 传空串

latchRequestThresholdReached 的注释是理解「幂等闩锁」语义的好例子:因为服务端状态永不回退,客户端可以放心重试,丢失的调用会在下次尝试时自愈(见 user.ts L33-L44)。

vault.ts:金库(密码保险库)的 SRP 校验流程

vault.ts 封装了 Insomnia 本地金库的服务端半程逻辑,共 5 个函数:

  • getVaultGET /v1/user/vault,读取已有金库的 salt
  • createVaultPOST /v1/user/vault,提交 { salt, verifier } 创建金库;
  • resetVaultPOST /v1/user/vault/reset,同样参数重置金库;
  • verifyVaultAPOST /v1/user/vault-verify-a,上传客户端计算的 srpA,返回 { sessionStarterId, srpB }
  • verifyVaultM1POST /v1/user/vault-verify-m1,提交 srpM1 与会话句柄,服务端返回 srpM2 供客户端二次校验。

srpA / srpB / srpM1 / srpM2 的字段命名表明它实现的是 SRP(Secure Remote Password)协议的两步交换:客户端先算 A 发上去,拿回 B 后本地算 M1,服务端验证后回 M2,双方由此在不传输密码的前提下确认密码正确。结合桌面端路由 auth.create-vault-keyauth.validate-vault-keyauth.reset-vault-key(如 auth.validate-vault-key.tsx)的存在,可以推断 UI 层的金库创建/校验流程正是驱动这一组函数完成的。

spaces.ts:v3 SDK 客户端 + 游标分页

spaces.ts 与其他模块不同,它不直接使用注入的 fetch,而是封装 @getinsomnia/insomnia-v3-fetch 生成的 OpenAPI SDK(DefaultApi):

interface V3ClientConfig {
  getBaseURL: () => string;
  getClientString: () => string;
  generateRequestId: () => string;
  // 代理感知的 fetch;否则 SDK 回退到全局 fetch,
  // 在 Electron 主进程 / CLI 中会绕过系统代理与 OS 证书
  fetchApi: FetchAPI;
}

export function configureV3Client(config: V3ClientConfig) { ... }

buildClient 用配置构造 ConfigurationbasePath${baseURL}/v3apiKey 回调把 X-Session-ID 注入请求,并统一附加 X-Insomnia-ClientX-Origininsomnia-request-id 三个头。这里的注释点明了与 configureFetch 相同的动机:让 SDK 请求也走代理感知的 fetch

包内唯一的业务函数 getSpacespageSize: 200 循环拉取当前用户所属的全部 space(空间),通过解析 meta.page.next 中的 page[after] 游标翻页直到耗尽;SDK 抛出的 ResponseError 会被 mapSdkError 转换成本包统一的 ResponseFailError,并把 Problem JSON 中的 title/detail 带出来。源码中保留的 TODO 也值得注意:作者计划让调用方改为按需分页而非一次性拉全量(见 spaces.ts L77-L83)。

organizations.ts:组织特征、席位、角色与权限

organizations.ts 是端点最多的模块之一,覆盖团队(组织)管理的核心读接口:

  • Organization 类型是 Space 的别名再导出,注释说明这是为了既有消费方的源码兼容;
  • checkSeatsPOST /v1/organizations/{orgId}/check-seats,给定邮件列表检查席位是否足够,返回 { isAllowed, code? }code 可能是 NEEDS_TO_UPGRADENEEDS_TO_INCREASE_SEATS
  • getOrganizationRolesGET /v1/organizations/roles
  • getOrganizationFeaturesGET /v1/organizations/{orgId}/features,返回 7 项功能开关(bulkImportgitSyncorgBasicRbacaiMockServersaiCommitMessagesaiMcpClientkonnectSync,每项含 enabled 与可选 reason)及 billing 状态;
  • getOrganizationStorageRuleGET /v1/organizations/{orgId}/storage-rule,返回 enableCloudSync / enableLocalVault / enableGitSync 三个存储开关及 isOverridden
  • getOrgUserPermissionsGET /v1/organizations/{orgId}/user-permissions,返回 15 个 Permission 权限位(如 own:organizationcreate:invitationleave:organization)到布尔值的映射;
  • 成员管理三件套:deleteOrganizationMemberDELETE .../members/{userId})、updateUserRolesPATCH .../members/{userId}/roles)、getOrganizationMemberRolesGET .../members/{userId}/roles)。

这些函数与桌面端路由一一对应(如 collaborators-check-seats.tsx、[members.userId.roles.tsx](https://gitcode.com/GitHubTrending/in/insomnia/blob/51015127010798268152ff47b8218829f6ffecb6/packages/insomnia/src/routes/organization.userId.roles.tsx](https://gitcode.com/GitHub_Trending/in/insomnia/blob/51015127010798268152ff47b8218829f6ffecb6/packages/insomnia/src/routes/organization.organizationId.members.$userId.roles.tsx?utm_source=gitcode_repo_files)),从源码结构看,路由文件就是这些 API 函数在 UI 层的直接消费者。

project.ts / collaborators.ts / invite.ts:团队项目与协作链路

project.ts 封装团队项目(team project)的 CRUD 与统计:

  • fetchTeamProjects / createTeamProject / updateTeamProject / deleteTeamProject,路径均在 /v1/organizations/{orgId}/team-projects 下;
  • updateGitProjectCountPATCH /v1/organizations/{orgId}/git-projects,上报 Git 同步项目数量。

collaborators.ts 走的是带 desktop 前缀的端点族,覆盖协作的完整生命周期:

  • getCollaboratorsGET /v1/desktop/organizations/{orgId}/collaborators,支持 per_page(默认 25)、pagefilter 三个查询参数,返回分页元数据 + Collaborator[]typeinvite / member / group,metadata 含角色、邮件、过期时间等);
  • searchCollaboratorsGET .../collaborators/search/{keyword}
  • startAddingCollaboratorsPOST .../collaborators/start-adding,提交 { teamIds, emails },服务端返回每个账号的 publicKey(JSON Web Key 字符串)等「协作指令」;
  • finishAddingCollaboratorsPOST .../collaborators/finish-adding,客户端用拿到的公钥加密项目密钥后,提交 { teamIds, keys, accountIds, roleId } 完成添加——这是一条典型的端到端加密协作链路:先取公钥、本地加密、再提交密文;
  • unlinkCollaboratorDELETE .../collaborators/{id}/unlink
  • getRealTimeCollaboratorsPOST /v1/organizations/{orgId}/collaborators,提交 { project, file } 获取在线协作者的实时状态(UserPresence,含分支、文件、团队等)。

invite.ts 处理邀请与密钥协调:

  • reinvitePOST .../invites/{invitationId}/reinvite)、updateInvitationRolePATCH .../invites/{invitationId})、revokeInvitationDELETE .../invites/{invitationId});
  • getMyProjectKeys / reconcileFileKeys:获取并协调「文件(项目)密钥」,涉及 ProjectKeyencKey)、ProjectMemberpublicKey)、MemberProjectKeyencSymmetricKey)等类型,说明密钥的分配与同步也是通过该包完成的。

mock.ts:Mockbin 服务

mock.ts 对接独立的 mockbin 服务(通过 origin: mockbinUrl 覆盖基址):

  • fetchMockbinLogsGET {mockbinUrl}/bin/log/{compoundId},带 insomnia-mock-method 头,返回 HAR 格式的请求日志(MockbinLogOutput);
  • upsertMockbinPUT {mockbinUrl}/bin/upsert/{compoundId},写入一条 Har.Response 作为 mock 响应。

这与桌面端 mock-server 路由(如 [mock-route.mockRouteId.tsx](https://gitcode.com/GitHubTrending/in/insomnia/blob/51015127010798268152ff47b8218829f6ffecb6/packages/insomnia/src/routes/organization.mockRouteId.tsx](https://gitcode.com/GitHub_Trending/in/insomnia/blob/51015127010798268152ff47b8218829f6ffecb6/packages/insomnia/src/routes/organization.organizationId.project.projectId.workspace.projectId.workspace.workspaceId.mock-server.mock-route.$mockRouteId.tsx?utm_source=gitcode_repo_files) 中对 upsertMockbinisApiError 的调用)相印证:UI 里编辑一条 mock 规则,最终就是调用这里的函数完成远端 upsert。

enterprise.ts / trial.ts / vcs.ts:配额、试用与 GraphQL 代理

  • enterprise.tsgetResourceUsageGET /v1/user/resource-usage,mock 配额与 autoPurchase 配置)、getOwnEnterprisesGET /v1/user/enterprises)、getAccountUsedSeatsGET /v1/accounts/seats,成员数/邀请数/已用/总量)、getEnterpriseLicenseUsageGET /v1/enterprise/{enterpriseId}/license-usage,含 free 额度字段);
  • trial.tsgetTrialEligibilityGET /v1/trials/eligibility)与 startTrialPOST /v1/trials/start);
  • vcs.tsrunVcsGraphQLPOST /graphql?{name} 的形式代理云端 GraphQL 操作,name 标识查询名,请求体为 { query, variables },返回 { data, errors }——Insomnia 的 Insomnia Sync(云端同步)正是通过这条 GraphQL 通道进行的。

测试与验证方式

包内自带 Vitest 单测,覆盖 SDK 封装(getSpaces 分页)与用户端点两条典型链路:

由于网络层是注入式的,测试只需 configureFetch 注入一个 stub 实现即可断言各函数的路径、方法与序列化行为,无需真实后端。在仓库中验证该包可执行(只读查看即可,不需要修改仓库):

cd packages/insomnia-api
npm run test        # vitest run
npm run type-check  # tsc --noEmit
npm run lint

小结

insomnia-api 的价值在于把「与 Insomnia Cloud 通信」这件事收敛成一个纯 TypeScript、零构建、依赖注入的库:

  1. 入口与类型全部从包根导出(src/index.ts),消费方一行 import ... from 'insomnia-api' 即可拿到函数与类型;
  2. 网络实现与包解耦configureFetch 一次性注入,桌面端两个入口(entry.client.tsxentry.main.ts)注入代理感知的 insomniaFetch,v3 SDK 侧则由 configureV3Client 完成类似注入;
  3. 错误模型统一ResponseFailError + isApiError 让 UI 路由层能一致地展示服务端错误;
  4. 领域划分清晰:user / vault / organizations / collaborators / invite / project / mock / enterprise / trial / spaces / vcs 共 11 个模块,端点与方法名一一对应,便于按功能检索与复用。

如果你要为 inso CLI 或新的客户端接入 Insomnia Cloud,这个包就是应当遵循的封装范式:注入自己的 Fetch 实现、按领域调用具名函数、用 isApiError 判定失败原因。

登录后查看全文
热门项目推荐
相关项目推荐