Backend Function:把业务逻辑放在它该在的地方——后端
企业级系统开发中,有些事情必须由后端来做:敏感数据的校验、权限控制、复杂业务逻辑、多表事务处理。前端可以做基础校验,但不能保证安全;SQL 可以查询数据,但很难实现复杂业务规则。
Backend Function 的价值,就是让开发者能够在后端执行自定义业务逻辑,同时保持和前端 SDK 的无缝协作。前端只需要调用接口,不需要关心后端的实现细节;后端通过 Backend Function 统一处理业务规则,确保数据安全和一致性。
💡 Backend Function 是 Lovrabet 平台的业务逻辑扩展层。它让标准的数据操作(增删改查)可以无限扩展,满足企业特有的业务需求,同时保持代码的清晰和可维护性。

为什么需要 Backend Function?
企业在构建业务系统时,经常遇到三类问题:
| 问题类型 | 痛点 | Backend Function 如何解决 |
|---|---|---|
| 数据安全 | 手机号唯一性校验、身份证号脱敏、权限控制——前端可以绕过,数据库约束不够灵活 | 提供先验函数和后验函数,在数据操作前后自动执行,业务规则无法被绕过 |
| 复杂业务逻辑 | 价格计算、库存扣减、订单状态流转——涉及多表操作、事务控制、复杂规则判断 | 支持完整的 JavaScript/TypeScript 编程能力,灵活实现任何业务逻辑 |
| 性能与可维护性 | 关联多张表的查询、批量处理、异步执行 | 提供独立端点(ENDPOINT),让复杂逻辑在后端高效执行,前端只需调用一个接口 |
Backend Function 是什么?
Backend Function 是 Lovrabet 平台的后端业务逻辑扩展能力。它允许开发者编写 JavaScript/TypeScript 代码,在 Lovrabet 服务端环境中执行,通过 context.client 访问数据集 API 和 SQL 执行能力。
从技术上看,Backend Function 是运行在 Lovrabet 服务端的 JavaScript 函数,具有以下特点:
- 服务端执行:代码在 Lovrabet 服务器上运行,安全可靠
- 完整编程能力:支持 JavaScript/TypeScript,可以编写复杂业务逻辑
- 数据库访问:通过
context.client.models访问数据集,通过context.client.sql执行 SQL - 用户上下文:通过
context.userInfo获取当前登录用户信息 - 三种类型:先验函数(Before)、后验函数(After)、独立端点(ENDPOINT)
它的意义不只是"能写后端代码",而是让业务逻辑可以在合适的位置统一管理。前端专注交互和展示,后端专注业务规则和数据处理,两者之间通过清晰的 API 接口协作——这正是企业系统能够长期演进的关键。
Backend Function 由什么组成?
如果把 Backend Function 拆开来看,它由三种类型的函数组成,分别解决不同场景的问题。
三种核心类型
1. 先验函数(Before 脚本 / RequestInception)
在数据操作执行前自动触发,用于校验和拦截。
适用场景:
- 数据唯一性校验:手机号、邮箱、身份证号等
- 业务规则验证:年龄限制、金额限制、库存检查等
- 权限控制:多租户隔离、角色权限、数据归属检查
- 自动补充数据:设置所属部门、租户ID、创建人等
⚡ 触发时机:
create、update、delete、filter等操作前
🚫 阻止能力:抛出异常即阻止操作执行,返回错误信息给前端
/**
* 客户创建先验函数 - 手机号唯一性校验
*/
export default async function beforeCreate(params, context) {
const TABLES = {
customers: "dataset_XXXXXXXXXX", // 客户数据集
};
const models = context.client.models;
// 校验手机号唯一性
if (params.phone) {
const existing = await models[TABLES.customers].filter({
where: { phone: { $eq: params.phone } },
});
if (existing.tableData && existing.tableData.length > 0) {
throw new Error("手机号已被使用,请使用其他手机号");
}
}
return params;
}2. 后验函数(After 脚本 / HOOK 后置脚本)
在数据操作执行后、返回给前端之前自动触发,用于数据处理。
适用场景:
- 敏感数据脱敏:手机号、身份证号、银行卡号等
- 动态计算字段:从生日计算年龄、从消费金额计算等级
- 数据格式转换:日期格式化、状态码转显示名称
- 关联数据补充:自动查询并填充关联表信息
⚡ 触发时机:
filter、getOne、create、update等操作后
✏️ 修改能力:可以修改返回的数据结构,前端拿到的是处理后的数据
/**
* 客户查询后验函数 - 数据脱敏
*/
export default async function afterFilter(params, context) {
if (params.tableData) {
params.tableData = params.tableData.map((customer) => {
// 手机号脱敏:138****8000
if (customer.phone) {
customer.phone = customer.phone
.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
}
// 计算年龄
if (customer.birthday) {
const today = new Date();
const birthDate = new Date(customer.birthday);
customer.age = today.getFullYear() - birthDate.getFullYear();
}
return customer;
});
}
return params;
}3. 独立端点(ENDPOINT)
独立的 API 端点,前端通过 BFF API 主动调用,用于复杂业务逻辑。
适用场景:
- 复杂业务流程:订单处理(库存扣减 + 价格计算 + 优惠券应用)
- 批量操作:批量导入、批量更新、批量删除
- 聚合统计:复杂报表、多维度分析
- 第三方集成:调用外部 API、处理回调
📞 调用方式:
client.bff.execute({ scriptName: "xxx", params: {...} })
💻 编程能力:完整的 JavaScript/TypeScript 编程,支持循环、条件、异常处理等
/**
* 订单处理 - Backend Function 独立端点
*/
export default async function processOrder(params, context) {
const TABLES = {
orders: "dataset_XXXXXXXXXX",
products: "dataset_YYYYYYYYYY",
};
const models = context.client.models;
const { items, couponCode } = params;
// 1. 检查库存
for (const item of items) {
const product = await models[TABLES.products].getOne(item.productId);
if (product.stock < item.quantity) {
throw new Error(`产品 ${product.name} 库存不足`);
}
}
// 2. 计算价格
let totalPrice = 0;
for (const item of items) {
const product = await models[TABLES.products].getOne(item.productId);
totalPrice += product.price * item.quantity;
}
// 3. 应用优惠券
if (couponCode) {
const discount = await calculateDiscount(couponCode, totalPrice);
totalPrice -= discount;
}
// 4. 创建订单
const orderId = await models[TABLES.orders].create({
items: JSON.stringify(items),
totalPrice,
status: "pending",
createTime: new Date().toISOString(),
});
// 5. 扣减库存
for (const item of items) {
const product = await models[TABLES.products].getOne(item.productId);
await models[TABLES.products].update(item.productId, {
stock: product.stock - item.quantity,
});
}
return { success: true, orderId, totalPrice };
}核心 API
数据集操作
const models = context.client.models;
// 查询单条
const record = await models[TABLES.customers].getOne(123);
// 查询列表
const result = await models[TABLES.customers].filter({
where: { status: { $eq: "active" } },
select: ["id", "name", "phone"],
orderBy: [{ createTime: "desc" }],
pageSize: 20,
});
// 创建
const id = await models[TABLES.customers].create({
name: "张三", phone: "13800138000",
});
// 更新
await models[TABLES.customers].update(123, { status: "inactive" });
// 删除
await models[TABLES.customers].delete(123);SQL 执行
// Backend Function 中 SQL 直接返回数组
const rows = await context.client.sql.execute({
sqlCode: "getUserStats",
params: { userId: "123" },
});
console.log(rows[0].orderCount);用户信息
const user = context.userInfo;
console.log(user.id); // 用户ID
console.log(user.username); // 用户名
console.log(user.tenantCode); // 租户编码
console.log(user.department); // 部门为什么它能让开发更简单?
传统模式下,实现一个"创建订单"功能需要:
| 步骤 | 传统方式 | 使用 Backend Function |
|---|---|---|
| 前端 | 基础校验(数量 > 0) | 基础校验 + 调用 BFF API |
| 后端 API | 接收请求 | 不需要单独写 API |
| 后端逻辑 | 检查库存、计算价格、应用优惠券、创建订单、扣减库存 | Backend Function 统一处理 |
| 数据库 | 执行多条 SQL,保证事务一致性 | 通过 SDK 操作,自动保证事务 |
核心优势:
- 🎯 代码集中:业务逻辑统一在后端,前端不需要关心实现细节
- 🔒 安全可控:敏感操作必须经过 Backend Function,无法绕过
- 🔧 易于维护:业务规则变更只需修改 Backend Function,前端无需改动
- ⚡ 性能优化:批量查询、缓存、异步处理等优化手段可以灵活使用
实际效果:
- 开发效率提升 2-3 倍:后端逻辑统一管理,避免前后端反复沟通
- 代码复用率提升 60%:相同的业务逻辑可以在多个接口中复用
- 维护成本降低 50%:业务规则集中在后端,修改不影响前端
典型应用场景
场景一:多租户数据隔离
企业内部有多个租户(子公司、事业部),每个租户只能看到自己的数据。
export default async function beforeFilter(params, context) {
const userTenantId = context.userInfo?.tenantCode;
if (!userTenantId) {
throw new Error("用户未分配租户,无法操作");
}
if (!params.where) { params.where = {}; }
params.where.tenant_id = { $eq: userTenantId };
return params;
}✅ 效果:前端查询时自动过滤,无需手动添加租户条件,确保数据安全。
场景二:敏感数据脱敏
客户列表需要显示手机号,但不能暴露完整号码。
export default async function afterFilter(params, context) {
if (params.tableData) {
params.tableData = params.tableData.map((customer) => {
if (customer.phone) {
customer.phone = customer.phone
.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
}
return customer;
});
}
return params;
}✅ 效果:前端拿到的数据已经是脱敏后的,无需额外处理,确保数据安全。
场景三:复杂订单处理
创建订单需要检查库存、计算价格、应用优惠券、扣减库存。
export default async function processOrder(params, context) {
const TABLES = {
orders: "dataset_XXXXXXXXXX",
products: "dataset_YYYYYYYYYY",
};
const models = context.client.models;
// 1. 检查库存 → 2. 计算价格 → 3. 应用优惠券 → 4. 创建订单 → 5. 扣减库存
return { success: true, orderId, totalPrice };
}前端调用:
const result = await client.bff.execute({
scriptName: "processOrder",
params: {
items: [{ productId: "p1", quantity: 2 }],
couponCode: "SUMMER2024",
},
});
console.log(result.orderId); // 订单ID✅ 效果:前端只需调用一个接口,后端统一处理所有业务逻辑,确保数据一致性。
写在最后
今天的企业数字化,业务逻辑越来越复杂。单纯的 CRUD 操作已经不能满足需求,企业需要的是一套能够在后端灵活实现业务规则的能力体系。
Backend Function 所做的,就是让业务逻辑可以放在它该在的地方——后端。它帮助企业把通用能力标准化(先验函数、后验函数),把复杂业务逻辑模块化(独立端点),让前端和后端可以各司其职,通过清晰的 API 接口协作——这正是企业系统能够长期演进的关键。
如何开始
- 学习先验函数(数据校验) → Backend Function(一):先验函数
- 学习后验函数(数据脱敏) → Backend Function(二):后验函数
- 学习独立端点(复杂业务逻辑) → Backend Function(三):独立接口
- 查看完整 API 文档 → BFF API 参考
- 需要技术支持 → 查阅常见问题 或 最佳实践,或联系你的商务经理