ToolJet Marketplace 插件开发实战:使用 tooljet CLI 从零构建 GitHub 数据源插件 ToolJet Marketplace 插件开发实战使用 tooljet CLI 从零构建 GitHub 数据源插件【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 项目地址: https://gitcode.com/GitHub_Trending/to/ToolJetToolJet 的可扩展性是其核心设计理念之一而 Marketplace 插件正是这种可扩展性的落地载体开发者可以用 JavaScript/TypeScript 编写数据源连接器插件并发布到 ToolJet Marketplace 供所有实例使用。本文以官方文档为主线结合本仓库中的真实实现带你用tooljetCLI 一步步创建一个基于 GitHub Personal Access Token 认证的 GitHub 数据源插件实现获取用户信息、仓库、Issue 与 Pull Request 等基础能力。读完本文你将掌握ToolJet 插件目录结构与四个核心文件manifest.json、operations.json、index.ts、icon.svg的作用、如何用 CLI 脚手架创建/删除插件、如何通过两个 schema 文件驱动前端动态 UI、如何用 Octokit 实现 QueryService 查询逻辑与错误处理以及最终如何将插件发布到 Marketplace。什么是 ToolJet 插件与 MarketplaceToolJet 的开发一直以可扩展性为中心允许开发者编写插件来扩展其能力。目前这些插件主要形态是连接器connectors——例如 PostgreSQL、MySQL、Twilio、Stripe 等数据源连接器。开发者可以使用 JavaScript/TypeScript 编写插件来增强 ToolJet 的功能并通过 ToolJet Marketplace 发布、分发这些插件。在发布侧Marketplace 插件的源码统一存放在本仓库的 marketplace/plugins 目录中在运行侧ToolJet 服务端通过读取插件注册表来决定加载哪些插件。该注册表即 plugins.json 文件其中记录了每个插件的名称、描述、版本、作者、时间戳等信息例如 GitHub 插件的注册条目大致如下{ name: GitHub, description: Plugin for GitHub APIs, version: 1.0.0, id: github, author: Tooljet, timestamp: Thu, 02 Mar 2023 11:52:32 GMT }当通过tooljetCLI 创建一个插件时CLI 会自动把上述格式的对象追加写入 plugins.json。ToolJet 服务端启动时会读取该文件并加载其中列出的所有插件。注意不要手工编辑 plugins.json。该文件由tooljetCLI 自动生成与维护手工改动可能导致插件在系统中无法正常工作。一个典型插件以 GitHub 为例的目录结构如下github/ package.json lib/ icon.svg index.ts operations.json manifest.json对照本仓库中 marketplace/plugins/github 的实际实现目录里除上述文件外还包含types.tsTypeScript 类型定义、query_operations.ts具体查询函数以及__tests__测试目录说明仓库在实践中文档所述结构的基础上又做了进一步拆分。各核心文件职责如下manifest.json描述插件数据源的名称、认证方式等元信息用于生成连接表单operations.json描述该数据源支持的全部操作及其参数用于在查询管理器中生成查询 UIindex.ts定义插件的QueryService负责处理查询执行、连接测试、连接缓存等核心逻辑icon.svg插件在界面中展示的图标package.json插件 npm 包定义通常由 CLI 自动生成之后可手工补充依赖。前置准备搭建 Marketplace 开发环境动手开发插件前需要先完成 Marketplace 的本地开发环境搭建详细步骤见 Marketplace 开发环境搭建指南核心要点如下1. 环境要求Node.jsv18.18.2npmv9.8.12. 在本地启动 ToolJet 并开启 Marketplace按环境选择合适的本地 Setup 指南macOS / Docker / Ubuntu启动 ToolJet 后需要在.env中配置以下环境变量变量取值作用ENABLE_MARKETPLACE_FEATUREtrue或false开启/关闭 Marketplace 功能开关ENABLE_MARKETPLACE_DEV_MODEtrue或false开发者模式每当包发生变化时自动构建插件同时会提供一个“刷新”按钮用于从文件系统重新加载已安装插件的最新本地改动极大方便开发迭代注意 Marketplace 默认是不开启的修改环境变量后需重启 ToolJet 实例。搭建完成后可通过/integrations路由访问 Marketplace。3. 安装 marketplace 依赖并构建在marketplace根目录执行cd marketplace npm install npm run build4. 安装 tooljet CLI管理 Marketplace 插件的创建、更新与删除都需要tooljetCLInpm install -g tooljet/cli # 验证安装成功 tooljet --version在 cli 目录的源码中可以看到 CLI 的插件子命令实现包括 cli/src/commands/plugin/create.ts创建插件、cli/src/commands/plugin/delete.ts删除插件与 cli/src/commands/plugin/install.ts安装插件。Step 1使用 CLI 创建一个 GitHub 插件在完成上述 Marketplace 环境搭建后即可开始插件开发。在终端执行# 创建新插件 tooljet plugin create github命令执行期间 CLI 会依次向你提问输入插件名称plugin name选择插件类型plugin type本示例选择api询问是否要为 marketplace 创建插件选择yes如果你的插件托管在 GitHub 上按提示提供仓库 URL否则留空即可。插件创建完成后CLI 会自动在 plugins.json 中登记该插件的元数据对象内容包含名称、描述、版本、作者及其他相关信息。脚手架模板同样存放在仓库中位于 marketplace/_templates/pluginCLI 正是基于这类模板生成插件骨架。Step 2用 manifest.json 定义连接表单连接表单即用户在 ToolJet 中新建数据源时填写的凭据表单由manifest.json驱动。为了让表单符合 GitHub 的认证需求需要在其中声明认证相关选项。文档给出的核心示例为properties部分properties: { credentials: { label: Authentication, key: auth_type, type: dropdown-component-flip, description: A single select dropdown to choose credentials, list: [ { value: personal_access_token, name: Use Personal Access Token } ] }, personal_access_token: { token: { label: Token, key: personal_token, type: password, description: Enter your personal access token, hint: You can generate a personal access token from your Github account settings. } } }上述 schema 声明了两个顶级字段credentials属性用于声明认证方式包含的键含义如下label面向用户的友好标签此处为 Authenticationkey认证方式的唯一标识值为auth_type将作为存储时的字段名type控件类型dropdown-component-flip表示一个可翻转展开方向的下拉选择器description字段用途说明list可用认证方式列表其每个对象的value存储值为personal_access_tokenname展示名为 Use Personal Access Token。personal_access_token属性声明了具体令牌输入框其下的token键包含label展示为 Tokenkey存储键为personal_tokentypepassword即密文输入框description提示文案 Enter your personal access tokenhint辅助提示建议用户从 GitHub 账户设置中生成 Personal Access Token。在manifest.json中可用的type控件类型包括type说明password密文输入框用于密码、Access Token 等敏感值dropdown-component-flip下拉菜单相对触发组件自动翻转展开方向text单行文本输入textarea多行文本输入toggle简单的开/关开关react-component-headers用于展示 React 组件分组标题codehinter代码输入框支持解析双花括号{{}}内的 JavaScript 表达式结合本仓库中 GitHub 插件的真实 manifest.json可以看到文档示例之外还有若干值得了解的字段source.name/source.kind/source.type声明数据源名称、唯一 kind如github与类型apisource.options声明认证字段的数据类型其中personal_token被标记为encrypted: true表示该凭据在服务端会加密存储defaults为字段提供默认值例如auth_type默认personal_access_tokenrequired声明必填字段数组例如[personal_token]。schema定义可参考仓库中的 manifest.schema.json对应的操作 schema 见 operations.schema.json。manifest.json 与前端 UI 的关系React 组件会读取manifest.json依据其 schema 动态生成连接表单的 UI 组件——文本输入框、下拉框、复选框等控件均由此渲染而来。文件中的properties定义了连接 API 或数据源所需的字段及其类型。Step 3用 operations.json 定义操作 schemaoperations.json描述某个特定数据源如 GitHub支持的全部操作及其参数ToolJet 查询管理器Query Manager依据它生成“新建查询”界面让用户选择操作并填写参数。文档中给出的 GitHub 插件示例properties如下properties: { operation: { label: Operation, key: operation, type: dropdown-component-flip, description: Single select dropdown for operation, list: [ { value: get_user_info, name: Get user info }, { value: get_repo, name: Get repository }, { value: get_repo_issues, name: Get repository issues }, { value: get_repo_pull_requests, name: Get repository pull requests } ] }, get_user_info: { username: { label: Username, key: username, type: codehinter, lineNumbers: false, description: Enter username, width: 320px, height: 36px, className: codehinter-plugins, placeholder: Enter username } }, get_repo: { owner: { label: Owner, key: owner, type: codehinter, lineNumbers: false, description: Enter owner name, width: 320px, height: 36px, className: codehinter-plugins, placeholder: developer }, repo: { label: Repository, key: repo, type: codehinter, lineNumbers: false, description: Enter repository name, width: 320px, height: 36px, className: codehinter-plugins, placeholder: tooljet } }, get_repo_issues: { owner: { label: Owner, key: owner, type: codehinter, lineNumbers: false, description: Enter owner name, width: 320px, height: 36px, className: codehinter-plugins, placeholder: developer }, repo: { label: Repository, key: repo, type: codehinter, lineNumbers: false, description: Enter repository name, width: 320px, height: 36px, className: codehinter-plugins, placeholder: tooljet }, state: { label: State, key: state, className: codehinter-plugins col-4, type: dropdown, description: Single select dropdown for choosing state, list: [ { value: open, name: Open }, { value: closed, name: Closed }, { value: all, name: All } ] } }, get_repo_pull_requests: { owner: { label: Owner, key: owner, type: codehinter, lineNumbers: false, description: Enter owner name, width: 320px, height: 36px, className: codehinter-plugins, placeholder: developer }, repo: { label: Repository, key: repo, type: codehinter, lineNumbers: false, description: Enter repository name, width: 320px, height: 36px, className: codehinter-plugins, placeholder: tooljet }, state: { label: State, key: state, type: dropdown, className: codehinter-plugins col-4, description: Single select dropdown for choosing state, list: [ { value: open, name: Open }, { value: closed, name: Closed }, { value: all, name: All } ] } } }operations.json的结构要点顶层的operation下拉框用于让用户选择具体操作其值对应后续各操作的字段组名称每个操作如get_user_info、get_repo、get_repo_issues、get_repo_pull_requests下都声明了执行该操作所需的参数字段参数控件大量使用codehinter类型——它支持解析{{ }}内的 JavaScript 表达式意味着用户可以引用应用中的其他变量如组件值、查询结果来动态填充参数对于state这类有枚举取值的参数则使用type: dropdownlist数组声明可选值open/closed/all。参考仓库中 GitHub 插件的真实 operations.json 可以发现实际 schema 比文档示例更丰富——例如get_repo_issues与get_repo_pull_requests还声明了page页码与page_size每页条数两个分页参数印证了同一套 schema 规则可自由扩展。两个 schema 文件的分工manifest.json被连接弹窗组件使用用于让用户填写数据源凭据operations.json被查询管理器使用用于在用户针对已连接的数据源创建查询时渲染参数表单。两者采用相同的 schema 约定。Step 4为插件安装 octokit npm 依赖查询逻辑将基于 GitHub 官方 SDKoctokit实现。切换工作目录到插件目录并以 workspace 方式安装# 切换到插件目录并安装 npm 包 npm i octokit --workspacetooljet-marketplace/github向某个插件安装 npm 包的一般形式是npm i npm-package-name --workspaceplugin-name-in-package-json--workspace标志用于在多包monorepo仓库中指定要安装包的特定 workspace。在这里包会被安装到名为tooljet-marketplace/github的 workspace 中。查看仓库中 GitHub 插件的 package.json可以确认其name正是tooljet-marketplace/github并且运行时依赖为tooljet-marketplace/common提供QueryService、QueryResult、QueryError等公共类型与基类与octokitbuild脚本为ncc build lib/index.ts -o dist即插件会被打包为单个dist入口文件打包产物声明为dist/index.js类型声明为dist/index.d.ts。Step 5在 index.ts 中实现查询执行逻辑index.ts定义了插件的QueryService负责处理查询执行的完整流程。它接收两类信息sourceOptions数据源信息包含连接凭据与配置。对于 GitHub 数据源即personal_token等认证信息queryOptions查询信息包含用户为本次查询选择的配置与参数如要拉取哪个用户/仓库的数据。QueryService 据此构造并执行对 GitHub API 的请求最终把结果返回给调用方继续处理。文档建议在插件源码目录下新建query_operations.ts将每个操作的请求函数独立成文件文档中目录写作plugins/github/src而本仓库实际的 GitHub 插件将其放在 marketplace/plugins/github/lib/query_operations.ts可理解为版本演进中目录命名的差异。核心代码如下import { Octokit } from octokit; import { QueryOptions } from ./types; export async function getUserInfo(octokit: Octokit, options: QueryOptions): Promiseobject { const { data } await octokit.request(GET /users/{username}, { username: options.username, }); return data; } export async function getRepo(octokit: Octokit, options: QueryOptions): Promiseobject { const { data } await octokit.request(GET /repos/{owner}/{repo}, { owner: options.owner, repo: options.repo, }); return data; } export async function getRepoIssues(octokit: Octokit, options: QueryOptions): Promiseobject { const { data } await octokit.request(GET /repos/{owner}/{repo}/issues, { owner: options.owner, repo: options.repo, state: options.state || all, }); return data; } export async function getRepoPullRequests(octokit: Octokit, options: QueryOptions): Promiseobject { const { data } await octokit.request(GET /repos/{owner}/{repo}/pulls, { owner: options.owner, repo: options.repo, state: options.state || all, }); return data; }query_operations.ts中每个函数负责一次具体查询由index.ts中的 QueryService 按需调用。实际仓库中的 query_operations.ts 还加入了分页与参数校验当传入page/page_size时会用validateNumber校验取值范围page ≥ 1page_size 在 1100 之间再映射为 GitHub API 的page与per_page参数。随后在index.ts中定义Github类并实现QueryService接口见 marketplace/plugins/github/lib/index.ts其中三个关键方法各司其职run(sourceOptions, queryOptions, dataSourceId)——执行查询的入口。内部先从queryOptions.operation解析出操作类型通过getConnection拿到已认证的 Octokit 客户端再用switch分发到getUserInfo/getRepo/getRepoIssues/getRepoPullRequests对应函数遇到未知操作或执行异常则抛出QueryError成功则返回{ status: ok, data: result }的QueryResult。testConnection(sourceOptions)——测试连接。在 ToolJet 应用中新建数据源时点击“测试连接”即触发该方法。它复用getConnection建立客户端然后调用octokit.rest.users.getAuthenticated()获取当前认证用户若请求成功返回{ status: ok }失败则返回{ status: failed, message: Invalid credentials }。在真实实现中这些方法还会先封装一层try/catch再返回结果。提示并非所有数据源都支持连接测试。如果该能力不适用于你的数据源可以在插件的manifest.json中加入customTesting: true来关闭“测试连接”按钮。getConnection(sourceOptions)——辅助函数。从sourceOptions.personal_token读取令牌并构造一个已认证的 Octokit 客户端async getConnection(sourceOptions: SourceOptions): Promiseany { const octokitClient new Octokit({ auth: sourceOptions.personal_token, }); return octokitClient; }其中SourceOptions、QueryOptions、Operation等类型统一定义在 types.ts 中。此外tooljet-marketplace/common见 marketplace/plugins/common为所有插件提供了QueryService、QueryResult、ConnectionTestResult、QueryError等公共契约。Step 6错误处理——把 errorDetails 回传给 Plugin SDK查询执行出错时必须把从 Plugin SDK 收到的错误信息返回给用户。为此需要在index.ts的run方法中构造并抛出带errorDetails的QueryError。需要注意的是错误的具体参数因插件而异Plugin SDK 中的data字段对应代码中的errorDetails动态生成的errorMessage对应错误预览中的description字段。以 MongoDB 场景为例若出现如下错误点击“测试”可看到 MongoDB 返回的完整错误预览可以这样实现错误处理catch (error) { let errorMessage An unknown error occurred; let errorDetails {}; if (error instanceof Error) { errorMessage error.message || errorMessage; errorDetails { name: error.name, code: (error as any).code || null, codeName: (error as any).codeName || null, keyPattern: (error as any).keyPattern || null, keyValue: (error as any).keyValue || null, }; } throw new QueryError(Query could not be completed, errorMessage, errorDetails); }这段代码确保错误消息与详情被正确送回 Plugin SDK从而让用户在查询错误预览中看到有意义的提示。错误在 ToolJet 查询编辑器中的预览效果如下从 index.ts 的实际实现可见错误处理的核心方式是一致的将原始错误对象封装进new QueryError(Query could not be completed, error.message, errorDetails)后抛出。插件内各查询函数的错误信息最终都会汇聚到这一层再由 ToolJet 前端呈现给用户。删除一个插件如需删除插件执行以下命令tooljet plugin delete PLUGIN_NAMECLI 在删除前会先询问确认该插件是否为 marketplace 插件确认无误后再继续删除操作。发布插件到 Marketplace插件开发完成后即可准备发布在 ToolJet 的 GitHub 仓库上提交一个 Pull Request创建插件的 PR。ToolJet 团队会进行 review若被批准插件将随下一个版本一同包含并发布到 Marketplace。小结从本文可以梳理出开发一个 ToolJet Marketplace 插件的完整闭环搭建 Marketplace 开发环境ENABLE_MARKETPLACE_FEATURE/ENABLE_MARKETPLACE_DEV_MODE用tooljet plugin create github生成脚手架编写manifest.json声明数据源认证 schema驱动连接弹窗 UI编写operations.json声明可执行操作与参数 schema驱动查询管理器 UI通过npm i pkg --workspacetooljet-marketplace/name安装 Octokit 等依赖在query_operations.ts中实现查询函数在index.ts中实现run/testConnection/getConnection完成 QueryService用QueryErrorerrorDetails做统一错误处理用tooljet plugin delete管理生命周期并通过提交 PR 发布到 Marketplace。本仓库中 marketplace/plugins/github 是这套流程的完整范本marketplace/plugins 目录下还有 OpenAI、Anthropic、Jira、Salesforce 等四十余个插件可以对照研读manifest.schema.json 与 operations.schema.json 则提供了两个 schema 文件的字段约束定义。掌握了这套流程后你几乎可以为任何 REST API 快速产出可安装、可发布、可复用的 ToolJet 数据源插件。【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考