场景导入:别再被“配置噩梦”拖累效率了
笔者在 N8N大学 里见过太多学员,面对数据库操作时的第一反应是:写个 API 接口吧,或者用那个笨重的数据库专用节点。其实,对于复杂的数据处理逻辑,n8n 的 Code 节点 才是真正的瑞士军刀。
手动写 SQL 语句拼接、处理复杂的 JSON 嵌套,不仅代码丑陋,维护起来更是噩梦。更别提在 n8n 中,直接使用 Code 节点连接数据库,能让你在一个节点内完成查询、转换、甚至事务提交,把原本需要 5 个节点的工作量压缩到 1 个。
本文将带你从零开始,利用 Code 节点(Node.js 环境)直接连接 MySQL/PostgreSQL,不仅搞定基础查询,更深入到实战必备的事务处理技巧。
准备工作:硬核条件清单
在开始敲代码之前,请确保你手头有以下资源。n8n 大学强调:工欲善其事,必先利其器。
- 一个正在运行的 n8n 实例:本地 Docker 或云服务器均可。
- 目标数据库信息:包括 Host、Port、User、Password 和 Database Name。
- 基础的 Node.js 知识:了解 Promise 和基本的 JavaScript 语法。
- 数据库驱动:n8n 的 Code 节点默认自带
mysql2和pg(PostgreSQL) 库,无需额外安装。
核心实操:从查询到事务的进阶之路
步骤一:建立基础连接(使用 MySQL 示例)
首先,我们在工作流中拖入一个 Code 节点。不要使用“仅返回数据”的模式,选择“为每个项运行一次”或“运行一次”取决于你的业务逻辑。
在 Code 节点的 JavaScript 编辑器中,我们需要引入驱动并建立连接。这里 N8N大学 给出一个封装好的连接函数,方便复用:
const mysql = require('mysql2/promise');
// 封装一个连接函数
async function queryDatabase(sql, params) {
// 这里的 credentials 建议从 n8n 的 Credentials 获取,为了演示直接写死
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'your_password',
database: 'test_db'
});
try {
const [rows, fields] = await connection.execute(sql, params);
return rows;
} finally {
await connection.end();
}
}
// 调用示例
const result = await queryDatabase('SELECT * FROM users WHERE id = ?', [1]);
// 输出到下个节点
return [{ json: { users: result } }];
注意: 如果你使用 PostgreSQL,只需将 require('mysql2/promise') 换成 require('pg'),并使用 new Client() 方式连接。
步骤二:处理动态参数(防止 SQL 注入)
硬编码 SQL 是菜鸟的做法。实战中,我们通常是从上游节点(如 Webhook 或 Form Trigger)获取数据。
假设上游节点传来了一个 userId,我们需要将其作为参数传入查询。利用 $input.first() 获取数据:
const mysql = require('mysql2/promise');
const connection = await mysql.createConnection({
host: 'localhost', user: 'root', password: '123456', database: 'test_db'
});
// 从上个节点获取动态 ID
const item = $input.first();
const userId = item.json.user_id;
// 使用 ? 占位符防止 SQL 注入
const sql = 'SELECT * FROM orders WHERE user_id = ?';
const [rows] = await connection.execute(sql, [userId]);
await connection.end();
return [{ json: { orders: rows } }];
步骤三:实战技巧——事务处理(Transaction)
这是 Code 节点的杀手锏。当你需要同时执行多条 SQL 语句,且要求“要么全成功,要么全失败”时(例如:扣减库存 + 生成订单),事务处理至关重要。
在 Code 节点中,我们利用 connection.beginTransaction()、connection.commit() 和 connection.rollback() 来控制。
const mysql = require('mysql2/promise');
const connection = await mysql.createConnection({
host: 'localhost', user: 'root', password: '123456', database: 'test_db'
});
try {
// 1. 开启事务
await connection.beginTransaction();
// 2. 执行第一条 SQL:扣减库存
const [res1] = await connection.execute(
'UPDATE products SET stock = stock - 1 WHERE id = ? AND stock > 0',
[101]
);
if (res1.affectedRows === 0) {
throw new Error('库存不足,回滚');
}
// 3. 执行第二条 SQL:生成订单
const [res2] = await connection.execute(
'INSERT INTO orders (product_id, created_at) VALUES (?, NOW())',
[101]
);
// 4. 提交事务
await connection.commit();
return [{ json: { success: true, orderId: res2.insertId } }];
} catch (error) {
// 5. 出错回滚
await connection.rollback();
return [{ json: { success: false, error: error.message } }];
} finally {
await connection.end();
}
这段代码展示了原子性操作的完整流程,是生产环境必须掌握的技巧。
避坑指南:实战中容易报错的细节
在 N8N大学 的过往案例中,Code 节点连数据库最常见的两个坑如下:
1. 时区不一致导致的时间错误
如果你的 n8n 运行在 Docker 容器中,而数据库在宿主机或另一时区,插入的时间可能会相差几个小时。
解决方案: 在创建连接时显式指定时区。
const connection = await mysql.createConnection({
host: 'localhost',
// ... 其他配置
timezone: '+08:00' // 强制设置为北京时间
});
2. 连接泄露(Connection Leak)
很多初学者在 Code 节点中建立了连接,但在报错时忘记关闭(connection.end())。当工作流频繁运行时,数据库连接数会瞬间爆满,导致服务不可用。
解决方案: 严格使用 try...finally 结构,确保无论成功还是失败,连接最终都会被关闭。
FAQ 问答
Q1: Code 节点连接数据库和 n8n 官方的 MySQL 节点有什么区别?
笔者回答: 官方的 MySQL 节点图形化配置简单,适合单表查询。但 Code 节点灵活性极高,支持复杂的多表 Join、事务处理、自定义逻辑封装,且不需要在界面上点击多次配置字段,直接写代码更高效。
Q2: Code 节点会暴露我的数据库密码吗?
笔者回答: 绝对会。因此,永远不要把密码硬编码在代码里。请使用 n8n 的 Credentials(凭据) 功能。在 Code 节点中,通过 $getWorkflowStaticData('global') 或者环境变量引用凭据。
Q3: 为什么不直接在 Code 节点里用 ORM(如 Sequelize)?
笔者回答: Code 节点的运行环境是受限的沙盒,虽然自带了基础驱动,但安装大型 ORM 库(如 Sequelize、TypeORM)非常困难且不推荐。对于自动化流程,原生 SQL 或轻量级驱动(mysql2/pg)性能更好,也更可控。
总结与资源
掌握 n8n Code 节点连接数据库,意味着你拥有了处理复杂业务逻辑的“核武器”。从基础查询到事务回滚,这套实战技巧能覆盖 90% 的数据库交互场景。
记住,自动化的核心不是为了偷懒,而是为了更优雅地解决问题。如果你在配置过程中遇到具体的报错,欢迎在 N8N大学 社区发帖,我们乐于帮你排忧解难。