ToolJet SendGrid 数据源接入实战:API 密钥连接与邮件发送查询配置详解
本文围绕 ToolJet 内置的 SendGrid 插件,系统讲解如何在 ToolJet 中新建 SendGrid 数据源、通过图形化查询编辑器调用 SendGrid v3 API 的 mail 端点发送邮件,并深入插件源码解释每个参数在底层的真实作用。读完本文,你将掌握 SendGrid 数据源从「建连 → 建查询 → 运行调试」的完整闭环,并能利用 {{ }} 表达式实现向单个或多个收件人发送单封/多封邮件的灵活编排。
SendGrid 数据源能做什么
ToolJet 官方文档将 SendGrid 插件定位为:连接你的 SendGrid 账户并向指定收件人发送电子邮件(见 sendgrid 数据源文档)。从源码角度看,该能力由独立插件包 plugins/packages/sendgrid 提供,其运行时通过 SendGrid 官方 Node 库 @sendgrid/mail 与 SendGrid v3 API 的 mail 端点交互。因此该数据源当前只支持「邮件服务」这一类操作,不支持读取邮件、管理联系人等其他 API 端点。
典型的落地场景包括:
- 在应用事件(按钮点击、查询成功后、定时任务)里触发发送通知邮件;
- 结合表格选中行、表单输入等组件数据,将动态收件人与动态正文拼进发送查询;
- 用一个查询同时把相同邮件发给多个收件人,或用
{{true}}开关让系统为每个收件人分别发送独立邮件。
前置条件:准备 SendGrid API Key
连接 SendGrid 数据源所需的唯一凭证是 SendGrid API Key。插件的数据源清单 manifest.json 明确要求该字段必填(required: ["api_key"]),并把字段声明为:
type: "password":界面中按密码框展示;encrypted: true:凭证在落库与传输时做加密处理;- 帮助文案指向 SendGrid 账户的 API Keys 管理页面,引导你前往生成 key。
也就是说,连接前请先登录 SendGrid 控制台,创建一枚具备 Mail Send 权限的 API Key,并保管好完整密钥串(SendGrid 只在创建时完整展示一次)。
建立与 SendGrid 的连接
在 ToolJet 中新增 SendGrid 数据源有两种入口:
- 在应用编辑器的查询面板上点击 + Add new data source 按钮;
- 从 ToolJet 仪表盘导航到 Data Sources(数据源总览)页面后添加。
随后在数据源配置表单中,为 API key 字段填入上一步生成的 SendGrid API Key,保存即完成连接。
从实现上看,数据源的身份结构非常简单——types.ts 中 SourceOptions 仅含 api_key: string 一个成员。运行查询时,查询服务 会执行 sgMail.setApiKey(sourceOptions.api_key) 把这枚 key 注入 SendGrid 官方客户端,作为后续请求的 Bearer 鉴权凭据。若 API Key 缺失或为空,查询会直接抛错 Missing API key,并在错误详情中提示 Query could not be completed as API key is not set,从源头阻断非法调用。
发起一次 SendGrid 邮件查询
数据源连接成功后,即可在查询面板中按以下四步创建并执行邮件发送:
- 点击编辑器底部查询管理器的 + Add 按钮新建查询;
- 选择上一步创建的 SendGrid 数据源;
- 在 Operation 下拉框中选择 Email service,并填写所需参数;
- 点击 Preview 按钮预览输出,或点击 Run 创建并触发查询。
操作类型下拉框由 operations.json 声明,当前仅有一个枚举项:mail_service(界面显示为 Email service)。
Email service 支持的参数
选好 Email service 操作后,表单会动态展开该操作的专属字段(由 operations.json 的 mail_service 分组驱动)。完整字段见下表:
| 类别 | 参数 | 字段 key | 类型/说明 | 默认值或示例 |
|---|---|---|---|---|
| 必需 | Multiple recipients | multiple_recipients |
布尔开关,决定多收件人时的发送策略 | {{false}} |
| 必需 | Send email to | send_mail_to |
收件人邮箱,支持字符串数组 | {{["dev@tooljet.io", "admin@tooljet.io"]}} |
| 必需 | Send email from | send_mail_from |
发件人邮箱,纯字符串 | admin@tooljet.io |
| 必需 | Subject | subject |
邮件主题 | 例如 Welcome to ToolJet |
| 必需 | Body as text | text |
纯文本正文 | 普通文本内容 |
| 可选 | Body as HTML | html |
HTML 富文本正文,非空时优先生成 HTML 邮件体 | 任意 HTML 片段 |
除文档列出的参数外,operations.json 还暴露了一个 Sender name(send_mail_from_name)可选字段,用于为发件人附加展示名称,这一点将在下文源码解析中说明。
关于两个关键字段的取值约定(对应原文 Info 提示):
- Send email to(
send_mail_to)——接受以逗号分隔的一组邮箱(数组形式),需要写成带{{ }}的 ToolJet 表达式。例如:也可以绑定组件数据,例如表格多选框的选中行邮箱集合:{{["dev@tooljet.io", "admin@tooljet.io"]}}{{tables.table1.selectedRows.map(r => r.email)}}。 - Send email from(
send_mail_from)——接受单个字符串。例如:admin@tooljet.io
两个「多收件人」语义务必分清
这是 SendGrid 数据源最容易踩坑的地方,原文以 Tip 形式给出了两条规则,其底层由 isMultiple 开关驱动(index.ts 中的 isMultiple: queryOptions.multiple_recipients ?? false):
- 发送一封邮件给多个收件人(默认):保持 Multiple recipients 字段为
{{false}}或留空,此时Send email to中填写的收件人数组会作为同一封邮件的收件人列表发出,所有收件人看到同一封邮件。 - 分别给多个收件人各发一封独立邮件:将 Multiple recipients 设置为
{{true}},此时系统会把Send email to里的收件人列表拆分成多封独立邮件逐个投递——适合需要按收件人单独计数、或正文包含个性化内容且每封邮件的 to 头必须唯一的场景。
需要留意 Multiple recipients 与正文个性化没有联动:即便开启该开关,正文本身仍按查询中写死的内容发送;若想做「每个收件人收到不同正文」,通常需要用循环或每次携带单个收件人的方式分别触发查询。
源码视角:查询参数如何被翻译成 SendGrid 请求
插件核心实现在 plugins/packages/sendgrid/lib/index.ts,其 run() 方法完整演示了从 ToolJet 查询参数到 SendGrid 邮件对象的映射过程,值得逐段推敲。
sgMail.setApiKey(sourceOptions.api_key);
const fromAddress =
queryOptions.send_mail_from_name && queryOptions.send_mail_from_name.trim()
? { email: queryOptions.send_mail_from, name: queryOptions.send_mail_from_name.trim() }
: queryOptions.send_mail_from;
const sendgridEmailOptions: EmailOptions = {
to: queryOptions.send_mail_to,
from: fromAddress,
subject: queryOptions.subject,
text: queryOptions.text,
isMultiple: queryOptions.multiple_recipients ?? false,
};
if (queryOptions.html && queryOptions.html.length > 0) {
sendgridEmailOptions.html = queryOptions.html;
}
result = await sgMail.send(sendgridEmailOptions);
几个值得注意的实现细节:
- Sender name 的组合逻辑:当
send_mail_from_name(即界面上的 Sender name)存在且去除首尾空格后不为空时,from会被构造成{ email: send_mail_from, name: send_mail_from_name }对象;否则直接退化为纯邮箱字符串。这与 types.ts 中from: string | { email: string; name: string }的联合类型一一对应。 - HTML 正文按需注入:只有
html参数非空字符串时才被加入邮件对象,否则邮件仅携带纯文本text。若你同时填写 text 与 html,客户端会按 SendGrid 约定同时生成两种 MIME 形态,由收件端自行选择渲染。 - 错误兜底:整段
sgMail.send包在 try/catch 中,失败时会把error.response打印到服务端日志并抛出自定义的QueryError('Query could not be completed', ...),便于在 ToolJet 查询结果面板看到可读错误。成功时返回{ status: 'ok', data: result }。 - 产物:最终调用的是
@sendgrid/mail的send()方法,请求目标是 SendGrid v3 的 mail 端点(相关类型约束见EmailOptions的 定义)。每次查询触发都是一次实时的出站邮件投递,而非离线任务。
用查询结果做后续联动
SendGrid 数据源的暴露变量由 manifest.json 声明:每次运行都会提供 isLoading、data 与 rawData 三个可引用变量。其中 isLoading 标识请求进行中状态,data 为发送结果对象。你可以在后续查询或组件事件中通过 {{queries.<查询名>.data}} 引用返回内容;若发送失败,data 中不会出现正常响应体,取而代之的是上面提到的 QueryError 提示。
一个常见组合示例:
- 表单组件收集
收件人、主题、正文; - 新建 SendGrid 查询,把收件人字段写成
{{ [form.data.email] }},主题写成{{ form.data.subject }},正文写成{{ form.data.body }}; - 在表单「提交」按钮的
On click事件中运行该查询,即可实现用户自助发信。
小结与注意事项
围绕本文内容,给出几条实用检查清单:
- 建连前:确认 SendGrid API Key 存在且具备 Mail Send 权限;key 在 ToolJet 中会被加密存储,但请勿在查询正文或日志中明文回显。
- 选参时:
send_mail_to必须写成数组(用{{ }}包裹),send_mail_from是普通字符串,二者类型不能混用。 - 批量时:同一封邮件群发保持
Multiple recipients = {{false}};逐封独立发送才置为{{true}}。 - 排错时:留意服务端日志中打印的
error.response(index.ts),它包含 SendGrid 返回的状态码与失败原因,比笼统的Query could not be completed更有诊断价值。
若需回顾数据源通用操作流程(环境、权限、管理),可继续阅读 Data Sources 总览文档;SendGrid 插件本体与配套 schema 可在 plugins/packages/sendgrid 目录下进一步研读。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00

