Syntax Sugar
This document shows how to use the SDK's safe and sqlSafe helpers to simplify error-handling code.
💡 Version requirement
The Traditional Way: Verbose and Error-Prone
Scenario 1: Regular API calls
TypeScript
// 📌 传统写法:两层嵌套
try {
const users = await client.models.users.filter();
console.log(users);
} catch (error) {
if (error instanceof LovrabetError) {
console.error(error.message, error.description);
} else {
console.error(error);
}
}Problems:
- try-catch adds nesting
- You have to check the error type manually
- More code, less readable
Scenario 2: SQL queries
TypeScript
// 📌 传统写法:三层检查
try {
const result = await client.sql.execute({ sqlCode: "xxx" });
// 业务层检查
if (result.execSuccess && result.execResult) {
console.log(`查询到 ${result.execResult.length} 条记录`);
result.execResult.forEach((row) => console.log(row));
} else {
console.error("SQL 执行失败");
}
} catch (error) {
if (error instanceof LovrabetError) {
console.error("请求失败:", error.message);
}
}Problems:
- HTTP errors plus business-logic errors — two layers of checks
- Nested access to
execResult - Repeated code, easy to forget a check
Scenario 3: Concurrent requests
TypeScript
// 📌 传统写法:错误处理分散
const [users, orders, stats] = await Promise.all([
client.models.users.filter().catch((e) => ({ error: e })),
client.models.orders.filter().catch((e) => ({ error: e })),
client.sql.execute({ sqlCode: "stats" }).catch((e) => ({ error: e })),
]);
if (users.error) console.error("获取用户失败");
if (orders.error) console.error("获取订单失败");
if (stats.error) {
console.error("获取统计失败");
} else {
if (!stats.data.execSuccess) {
console.error("SQL 执行失败");
}
}Problems:
- Every request needs its own error handling
- The SQL business-status check is even messier
- Inconsistent code structure
safe: Simplify Regular API Error Handling
TypeScript
import { safe } from "@lovrabet/sdk";
// 💡 语法糖:一次检查
const { data, error } = await safe(() => client.models.users.filter());
if (error) {
console.error("查询失败:", error.message, error.description);
return;
}
// data 直接是结果数据
console.log(data);Comparison:
| Traditional | safe |
|---|---|
| Nested try-catch | Flat destructuring |
| Manual error type checks | Automatic conversion to LovrabetError |
| 5-10 lines of code | 3 lines of code |
Concurrent requests: a consistent structure
TypeScript
// ✅ 简洁:统一处理
const [usersResult, ordersResult, statsResult] = await Promise.all([
safe(() => client.models.users.filter()),
safe(() => client.models.orders.filter()),
safe(() => client.sql.execute({ sqlCode: "stats" })),
]);
// 逐个检查
if (usersResult.error) console.error("用户失败");
if (ordersResult.error) console.error("订单失败");
if (statsResult.error) console.error("统计失败");
// 使用成功的数据
usersResult.data?.forEach((user) => console.log(user));sqlSafe: Built for SQL
TypeScript
import { sqlSafe } from "@lovrabet/sdk";
// ✅ 简洁:一次检查,直接拿到数组
const { data, error } = await sqlSafe(() =>
client.sql.execute({ sqlCode: "user-stats" })
);
if (!error) {
// data 直接是查询结果数组
console.log(`查询到 ${data.length} 条记录`);
data.forEach((row) => console.log(row));
}Comparison:
| Traditional | sqlSafe |
|---|---|
| try-catch plus execSuccess checks | A single if |
| Access result.execResult | Access data directly |
| 10-15 lines of code | 5 lines of code |
Typed SQL queries
TypeScript
interface UserStat {
id: number;
name: string;
login_count: number;
}
const { data, error } = await sqlSafe<UserStat>(() =>
client.sql.execute<UserStat>({ sqlCode: "user-stats" })
);
if (error) return;
// data 是 UserStat[],类型安全
data.forEach((stat) => {
console.log(`${stat.name}: ${stat.login_count} 次登录`);
});Helper Comparison Table
| Scenario | Traditional (lines of code) | Helper (lines of code) |
|---|---|---|
| Regular API error handling | 8-10 | 3 |
| Full SQL query handling | 15-20 | 5 |
| Concurrent requests | 30+ | 10 |
| Type-safe access | Type assertions required | Inferred automatically |
Best Practices
1. Prefer the helpers
TypeScript
// 💡 语法糖:简洁
const { data, error } = await safe(() => api.call());
if (error) return;
// 📌 传统写法:繁琐
try {
const data = await api.call();
} catch (e) {
if (e instanceof LovrabetError) {
// ...
}
}2. Always use sqlSafe for SQL
TypeScript
// 💡 语法糖:一次检查
const { data, error } = await sqlSafe(() => client.sql.execute(...));
// ❌ 避免:容易遗漏业务检查
const result = await client.sql.execute(...);
result.execResult?.forEach(...); // 可能 execSuccess=false3. Early returns
TypeScript
const fetchUsers = async () => {
const { data, error } = await safe(() => client.models.users.filter());
if (error) return { success: false, error: error.message };
return { success: true, data };
};4. Handle concurrent requests uniformly
TypeScript
const loadDashboard = async () => {
const [users, orders, stats] = await Promise.all([
sqlSafe(() => client.sql.execute({ sqlCode: "users" })),
sqlSafe(() => client.sql.execute({ sqlCode: "orders" })),
sqlSafe(() => client.sql.execute({ sqlCode: "stats" })),
]);
if (users.error || orders.error || stats.error) {
console.error("数据加载失败");
return;
}
return {
users: users.data!,
orders: orders.data!,
stats: stats.data!,
};
};API Reference
safe
TypeScript
function safe<T>(fn: Promise<T> | (() => Promise<T>)): Promise<SafeResult<T>>;
interface SafeResult<T> {
data: T | null;
error: LovrabetError | null;
}sqlSafe
TypeScript
function sqlSafe<T>(
fn: Promise<SqlExecuteResult<T>> | (() => Promise<SqlExecuteResult<T>>)
): Promise<SqlSafeResult<T>>;
interface SqlSafeResult<T> {
data: T[] | null;
error: LovrabetError | null;
}Related Documentation
- Error handling guide - the error handling mechanism in depth
- SQL API reference - full SQL API details
- API reference - complete API documentation