)
ToolJet 插件开发指南使用 tooljet CLI 从零创建并发布 Marketplace 数据源插件以 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/ToolJet本篇指南以 ToolJet 3.0.0-LTS 官方文档《Marketplace: Creating plugins》为主体结合当前仓库中marketplace/plugins/github的真实插件实现与cli/src/commands/plugin/create.ts的 CLI 源码完整讲解如何用tooljetCLI 引导bootstrap一个插件、编写manifest.json与operations.json双 Schema、实现 QueryService 查询逻辑与错误处理以及如何删除、发布插件。读完本文你将掌握 ToolJet 插件体系的核心文件结构与完整开发闭环能够独立为 ToolJet Marketplace 开发一个可运行、可测试、可发布的 API 类数据源插件。什么是 ToolJet 插件ToolJet 的开发一直围绕**可扩展性extensibility展开插件机制允许开发者扩展 ToolJet 的能力边界。目前插件主要被限定为连接器connectors**形态例如 PostgreSQL、MySQL、Twilio、Stripe 等数据源连接器。开发者可以使用 JavaScript/TypeScript 编写插件来增强 ToolJet 的功能并将其发布到 ToolJet Marketplace 供所有实例安装使用。从当前仓库的结构看所有 Marketplace 插件都存放在仓库根目录的 marketplace/plugins 下每个插件一个独立目录而官方内置插件则存放在 plugins/packages 下。两者的插件契约Plugin SDK是同一套接口定义位于 marketplace/plugins/common/lib。Step 1使用 tooljet CLI 创建新插件GitHub 示例前置条件Marketplace 开发环境在动手之前请先参考 Marketplace: Development Setup 完成环境准备克隆 ToolJet 仓库并在本地完成 MacOS / Docker / Ubuntu 任一方式的环境搭建在.env文件中启用两个环境变量并重启实例变量取值作用ENABLE_MARKETPLACE_FEATUREtrue/false控制用户是否可以使用 Marketplace默认关闭ENABLE_MARKETPLACE_DEV_MODEtrue/false开发模式包内容变化时自动构建并提供刷新按钮从文件系统重新加载已安装插件的本地改动在marketplace根目录安装依赖并构建cd marketplace npm install npm run build全局安装 tooljet CLI 并验证npm install -g tooljet/cli tooljet --version完成以上步骤后即可进入插件开发。执行创建命令在仓库根目录即 ToolJet 主目录执行# create a new plugin tooljet plugin create github命令执行后 CLI 会依次交互询问plugin name插件显示名称plugin type本例选择api是否创建 Marketplace 插件选择yesrepository URL如果你的插件托管在 GitHub 上请提供仓库 URL否则留空。从 cli/src/commands/plugin/create.ts 的源码可以确认该命令的行为细节插件类型支持database、api、cloud-storage三选一Flags.string({ options: [database, api, cloud-storage] })若未通过--type传入会使用 inquirer 弹出类型选择列表插件名与显示名均不能是纯数字否则直接报错退出命令内部调用 hygen 模板引擎以 marketplace/_templates/plugin 为模板生成插件骨架命令会校验当前目录必须存在marketplace、docs与marketplace/_templates目录确保你是在 ToolJet 仓库根目录内运行。CLI 做了什么plugins.json 注册表当插件通过 CLI 创建成功后一个描述该插件元数据的对象会被写入plugins.json文件位于server/src/assets/marketplace/目录。该对象包含插件的名称、描述、版本、作者等信息。plugins.json是 ToolJet 所有可用插件的注册表ToolJet 服务启动时会读取该文件并加载其中列出的所有插件。:::info 注意plugins.json不应被手动编辑——它由 ToolJet CLI 自动生成维护。手工改动可能导致插件无法在系统中正常工作。 :::插件目录结构一个典型的 ToolJet 插件目录结构如下对应本仓库 marketplace/plugins/githubgithub/ package.json lib/ icon.svg index.ts operations.json manifest.json各文件职责文件职责manifest.json描述插件的名称、描述等元信息并定义连接表单的 Schemaoperations.json定义插件支持的**全部操作operation**及其参数元数据index.ts实现插件的主文件创建QueryService负责查询执行、连接测试、缓存等icon.svg插件的图标package.json由 CLI 自动生成的包清单为什么需要 manifest.json 与 operations.json这两个文件是 ToolJet 动态 UI 体系的基石二者使用相似的 Schema 结构manifest.json被连接弹窗组件connection modal使用用于为用户输入数据源凭据生成动态连接表单。它定义了 API 或数据源的 Schema包括名称、类型、暴露的变量以及认证方式和其他可配置属性。properties段指明了连接所需字段及其类型React 组件据此渲染文本输入框、下拉框、复选框等 UI 元素。operations.json被查询管理器query manager使用在用户针对已连接数据源生成具体查询时加载。它描述可执行的操作及其参数React 组件据此生成操作选择下拉框和参数输入区用户在界面上完成参数填写后组件将其组装成可对数据源执行的最终查询并把结果返回给用户。:::tip manifest.json 面向连接数据源operations.json 面向编写查询两者 Schema 同构理解其一即可触类旁通。 :::Step 2定义 manifest.json连接表单为了构建连接表单需要在 manifest.json 中声明必要的选项。以下是当前仓库 marketplace/plugins/github/lib/manifest.json 中properties的实际内容与文档示例一致properties: { credentials: { label: Authentication, key: auth_type, type: dropdown-component-flip, description: Single select dropdown for choosing credentials, list: [ { value: personal_access_token, name: Use Personal Access Token } ] }, personal_access_token: { token: { label: Token, key: personal_token, type: password, description: Enter personal access token, hint: You can generate a personal access token from your GitHub account settings. } } }该文件定义了认证选项一个选择凭据类型的下拉框以及一个输入 Personal Access Token 的密码框。label、key、type、description、hint等属性共同刻画了连接 API/数据源所需的字段及其类型。两个核心属性的语义credentials属性指定认证方式包含如下键label认证方式的用户友好名称值为 Authenticationkey认证方式的唯一标识值为auth_type该 key 会作为数据源配置项被存储与读取type认证方式的控件类型值为dropdown-component-flipdescription认证方式的说明文字list可选认证方式的数组。本例只有一项 Personal Access Token其中value为personal_access_tokenname为 Use Personal Access Token。personal_access_token属性指定该认证方式的具体凭据字段其token键包含labelTokenkeypersonal_token源码 marketplace/plugins/github/lib/types.ts 中SourceOptions的personal_token: string即消费该 keytypepassword输入框以掩码形式展示descriptionEnter personal access tokenhintYou can generate a personal access token from your GitHub account settings.可用的 type 选项password输入秘密值如密码或访问令牌dropdown-component-flip创建下拉菜单位置相对触发组件翻转text单行文本输入textarea多行文本输入toggle简单的开/关开关react-component-headers为 React 组件显示标题codehinter用于输入代码的专用输入框支持在双花括号{{}}内解析 JavaScript 代码可实现表达式/动态值绑定。manifest.json 的完整结构补充查看仓库中的真实文件manifest.json除了properties外还包含若干顶层字段理解它们有助于写出规范清单{ $schema: https://raw.githubusercontent.com/ToolJet/ToolJet/develop/plugins/schemas/manifest.schema.json, title: GitHub datasource, description: A schema defining GitHub datasource, type: api, source: { name: GitHub, kind: github, exposedVariables: { isLoading: false, data: {}, rawData: {} }, options: { auth_type: { type: string }, personal_token: { type: string, encrypted: true } } }, defaults: { auth_type: { value: personal_access_token }, personal_token: { value: } }, required: [personal_token] }source.options声明数据源配置项的运行时类型encrypted: true表示该字段personal_token在服务端加密存储defaults提供默认值required声明必填字段对应 JSON Schema 定义见仓库 plugins/schemas/manifest.schema.json。Step 3定义 operations.json查询操作operations.json声明数据源如 GitHub支持的全部操作及其参数 Schema。以get_repo_pull_requests为例完整内容见 marketplace/plugins/github/lib/operations.jsonproperties: { 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, placeholder: developer }, repo: { label: Repository, key: repo, type: codehinter, placeholder: tooljet } }, get_repo_issues: { owner: { label: Owner, key: owner, type: codehinter }, repo: { label: Repository, key: repo, type: codehinter }, 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 } ] } }, get_repo_pull_requests: { owner: { label: Owner, key: owner, type: codehinter }, repo: { label: Repository, key: repo, type: codehinter }, state: { label: State, key: state, type: dropdown, className: codehinter-plugins col-4, list: [ { value: open, name: Open }, { value: closed, name: Closed }, { value: all, name: All } ] } } }operations.json详细说明了可执行操作的类型、执行操作所需的字段及各字段的数据类型。上述 4 个操作的value与 marketplace/plugins/github/lib/types.ts 中定义的Operation枚举一一对应export enum Operation { GetUserInfo get_user_info, GetRepo get_repo, GetRepoIssues get_repo_issues, GetRepoPullRequests get_repo_pull_requests, }get_repo_issues与get_repo_pull_requests在仓库实现中还额外支持分页参数page_size每页条数默认 30与page页码默认 1这两个参数在 marketplace/plugins/github/lib/query_operations.ts 中会经过validateNumber校验后映射为 GitHub API 的page与per_page查询参数。Step 4为插件安装 npm 依赖进入插件目录并安装所需的 npm 包。由于仓库采用 npm workspace 管理多包需要通过--workspace指定目标插件# change directory to the plugin directory and install the npm package npm i octokit --workspacetooljet-marketplace/github通用安装语法为npm i npm-package-name --workspaceplugin-name-in-package-json--workspace标志用于在多包仓库中指定安装目标工作区。这里将octokitGitHub REST API 官方客户端安装进tooljet-marketplace/github工作区。从 marketplace/plugins/github/package.json 可以看到该插件的依赖还包括插件 SDK 包tooljet-marketplace/common构建脚本使用ncc build lib/index.ts -o dist将 TypeScript 打包为单一产物。Step 5在 index.ts 中实现查询执行逻辑QueryService 职责index.ts需要为 GitHub 插件实现QueryService。QueryService 负责查询的整个执行过程接收数据源相关信息——包括凭据、配置与查询参数。具体到 GitHub 数据源sourceOptions包含认证凭据例如 Personal Access TokenqueryOptions包含具体查询的配置与参数例如获取某个用户的仓库列表。QueryService 利用这些信息构造并执行针对 GitHub API 的请求将结果数据返回给调用方做进一步处理。编写 query_operations.ts在marketplace/plugins/github/lib目录创建query_operations.ts仓库中位于 marketplace/plugins/github/lib/query_operations.ts每个函数对应一个操作内部通过 Octokit 调用 GitHub REST APIimport { 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 调用。当前仓库实现在 issues / pull requests 上还增加了分页支持page最小值为 1page_size取值 1100并对非法输入抛出带自定义提示的Error。实现 QueryService 的三个方法在index.ts中定义Github类完整实现见 marketplace/plugins/github/lib/index.ts实现QueryService接口接口定义见 marketplace/plugins/common/lib/query_service.interface.ts核心是三个方法run(sourceOptions, queryOptions, dataSourceId)执行查询的主方法。它根据queryOptions.operation分发到query_operations.ts中对应的函数最终返回符合QueryResult类型定义见 marketplace/plugins/common/lib/query_result.type.ts的结果对象async run(sourceOptions: SourceOptions, queryOptions: QueryOptions, dataSourceId: string): PromiseQueryResult { const operation: Operation queryOptions.operation; const octokit: Octokit await this.getConnection(sourceOptions); let result {}; try { switch (operation) { case Operation.GetUserInfo: result await getUserInfo(octokit, queryOptions); break; case Operation.GetRepo: result await getRepo(octokit, queryOptions); break; case Operation.GetRepoIssues: result await getRepoIssues(octokit, queryOptions); break; case Operation.GetRepoPullRequests: result await getRepoPullRequests(octokit, queryOptions); break; default: throw new QueryError(Query could not be completed, Invalid operation, {}); } } catch (error) { throw new QueryError(Query could not be completed, error.message, {}); } return { status: ok, data: result, }; }testConnection(sourceOptions)在 ToolJet 应用中添加数据源时用于测试连接。它通过获取当前认证用户octokit.rest.users.getAuthenticated()来验证凭据有效性返回ConnectionTestResult定义见 marketplace/plugins/common/lib/connection_test_result.type.tsasync testConnection(sourceOptions: SourceOptions): PromiseConnectionTestResult { const octokit await this.getConnection(sourceOptions); try { const { status } await octokit.rest.users.getAuthenticated(); if (status) { return { status: ok }; } } catch (error) { return { status: failed, message: Invalid credentials }; } }:::note 并非所有数据源都支持连接测试。如果该能力不适用于你的数据源可以在插件的 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; }类型定义types.tsSourceOptions与QueryOptions的类型定义位于 marketplace/plugins/github/lib/types.tsexport type SourceOptions { auth_type: string; personal_token: string; }; export type QueryOptions { operation: Operation; username?: string; repo?: string; owner?: string; state?: open | closed | all; page_size?: string; page?: string; };Step 6添加错误处理发生错误时必须把从插件 SDK 收到的错误信息返回给上层。为此需要在index.ts的run方法中携带errorDetails具体错误参数因插件而异。对应关系如下插件 SDK 中的data字段 ↔ 代码中的errorDetails动态生成的errorMessage↔ 错误预览中的description字段。以 MongoDB 插件为例当出现诸如凭据错误、唯一键冲突duplicate key之类的异常时可按如下方式实现错误处理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); }这段代码确保错误消息与错误详情被正确传递回插件 SDK从而在查询错误预览中呈现有意义的信息。QueryError是插件 SDK 提供的标准错误类型定义见 marketplace/plugins/common/lib/query.error.ts其构造签名为(message, description, data)分别对应错误标题、错误描述与结构化的错误详情。删除一个插件删除插件使用如下命令tooljet plugin delete PLUGIN_NAMECLI 会先询问确认待删除的插件是否为 Marketplace 插件确认后才继续删除对应实现见 cli/src/commands/plugin/delete.ts。发布一个插件插件开发完成后在 ToolJet 的 GitHub 仓库上提交一个Pull Request即可发起发布。ToolJet 团队会评审该 PR评审通过后插件将随下一个版本一起合入并发布到 Marketplace。从本仓库可以观察到当前 Marketplace 已包含 github 等大量插件涵盖人工智能、云存储、数据库、SaaS 等类别见 marketplace/plugins新插件遵循相同的目录规范与 Schema 约束即可无缝加入生态。开发过程中可参照 marketplace/plugins/github/tests/index.js 的测试样例验证 QueryService 行为保证插件在合入前的质量。小结本文从 ToolJet 3.0.0-LTS 的官方插件开发文档出发结合仓库中 GitHub 插件的真实代码完整覆盖了插件开发闭环环境准备 → CLI 引导创建 →manifest.json连接表单→operations.json查询操作→ 依赖安装 → QueryService 三方法实现 → 错误处理 → 删除与发布。核心要点可归纳为双 Schema 驱动动态 UImanifest.json驱动连接表单operations.json驱动查询编辑器两者 Schema 同构QueryService 是插件的执行核心run分发操作、testConnection验证连接、getConnection构造认证客户端标准错误协议通过QueryError(message, description, data)向插件 SDK 传递可读的错误预览CLI 托管生命周期创建、删除均由tooljetCLI 管理plugins.json注册表自动维护切勿手改。【免费下载链接】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),仅供参考