Skip to content

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:

Traditionalsafe
Nested try-catchFlat destructuring
Manual error type checksAutomatic conversion to LovrabetError
5-10 lines of code3 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:

TraditionalsqlSafe
try-catch plus execSuccess checksA single if
Access result.execResultAccess data directly
10-15 lines of code5 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

ScenarioTraditional (lines of code)Helper (lines of code)
Regular API error handling8-103
Full SQL query handling15-205
Concurrent requests30+10
Type-safe accessType assertions requiredInferred 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=false

3. 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;
}

  • Error handling guide - the error handling mechanism in depth
  • SQL API reference - full SQL API details
  • API reference - complete API documentation

基于飞书知识库同步生成,内容以飞书源文档为准