API Usage Guide
This document details the API usage methods of Lovrabet SDK, including CRUD operations, query parameters, response handling, and other core features.
Before starting, please ensure you have completed SDK Configuration. It is recommended to use CLI auto-generated configuration.
The following examples use the standard method client.models.dataset_xxx, but you can also use the alias method client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df (functionally identical).
đ Basic CRUD Operationsâ
Query Listâ
// Basic query
const response = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter();
// Query with parameters
const response = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter({
currentPage: 1, // Current page number
pageSize: 20, // Records per page
status: "active", // Custom query conditions
keyword: "search-term", // Keyword search
});
// Response structure
console.log(response.tableData); // Data list
console.log(response.total); // Total records
console.log(response.currentPage); // Current page
console.log(response.pageSize); // Page size
Get Single Recordâ
const user = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.getOne("user-id");
console.log(user); // User detail object
Create Recordâ
const newUser = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.create({
name: "Jane Doe",
email: "jane@example.com",
status: "active",
});
console.log("Newly created user ID:", newUser.id);
Starting from v1.1.14, OpenAPI mode also supports create() operation. Previous versions only supported WebAPI mode.
Update Recordâ
const updatedUser = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.update("user-id", {
name: "Jane Smith",
email: "jane.smith@example.com",
});
console.log("Updated user:", updatedUser);
Starting from v1.1.14, OpenAPI mode also supports update() operation. Previous versions only supported WebAPI mode.
Delete Recordâ
await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.delete("user-id");
console.log("User deleted");
delete() operation is only supported in WebAPI mode (Cookie authentication). OpenAPI mode does not currently support delete operations.
Export Excel v1.1.24+â
Export dataset as Excel file, returns a downloadable file URL.
// Export all data
const fileUrl = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.excelExport();
console.log(fileUrl);
// 'https://yuntoo-export-import.oss-cn-hangzhou.aliyuncs.com/xxx.xlsx?...'
// Open download in browser
window.open(fileUrl, '_blank');
// Export with filter conditions
const fileUrl = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.excelExport({
status: 'active',
createTime: '2025-01-01'
});
window.open(fileUrl, '_blank');
excelExport() operation is only supported in WebAPI mode (Cookie authentication). OpenAPI mode does not currently support this feature.
Get Select Options v1.1.18+â
Used to get dropdown option data for data tables, suitable for Select, Radio, Checkbox and other form components.
getSelectOptions() allows data tables to provide their own data as dropdown options, rather than querying other tables.
Correct Understanding:
client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.getSelectOptions()- Queries users table's own data, generates user list options- These options can be used in: order table's "Select User" field, article table's "Select Author" field, etc.
Common Misconception:
- â Calling
client.models.dataset_a1b2c3d4e5f6789012345678abcdef12.getSelectOptions()in order table to get user list - â
Should call
client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.getSelectOptions()to get user list
Core Principle: Whose data, who provides options
This reflects important software design principles:
- đī¸ Single Responsibility Principle (SRP) - Each model only manages and provides its own data
- đ Dependency Inversion Principle (DIP) - Order table depends on user table's provided interface, not direct coupling
- đ¯ Separation of Concerns (SoC) - Clear separation of responsibilities between data providers and consumers
- đĻ Service Provider Pattern - Tables as data sources, providing standardized data services externally
This design makes the system easier to maintain, extend, and test.
// Get dropdown options for user table (query users table's own data)
const userOptions = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.getSelectOptions({
code: "user_id", // Field name used as option value
label: "user_name", // Field name used as display text
});
// Returns standard format
console.log(userOptions);
// [
// { label: 'Zhang San', value: 'user001' },
// { label: 'Li Si', value: 'user002' },
// { label: 'Wang Wu', value: 'user003' }
// ]
// Use in React
import { Select } from "antd";
const { Option } = Select;
function UserSelector() {
const [options, setOptions] = useState([]);
useEffect(() => {
loadOptions();
}, []);
const loadOptions = async () => {
const data = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.getSelectOptions({
code: "id",
label: "name",
});
setOptions(data);
};
return (
<Select placeholder="Select User">
{options.map((option) => (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
))}
</Select>
);
}
Real Application Scenarios:
// Scenario 1: Select user in order form
// Call users table to provide user options
const userOptions = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.getSelectOptions({
code: "user_id",
label: "user_name",
});
// Used in: <Select>Select Order User</Select>
// Scenario 2: Select category in article form
// Call categories table to provide category options
const categoryOptions = await client.models.dataset_c1d2e3f4a5b6789012345678abcdef34.getSelectOptions({
code: "category_id",
label: "category_name",
});
// Used in: <Select>Select Article Category</Select>
// Scenario 3: Select department in employee form
// Call departments table to provide department options
const deptOptions = await client.models.dataset_d1e2f3a4b5c6789012345678abcdef56.getSelectOptions({
code: "dept_id",
label: "dept_name",
});
// Used in: <Select>Select Department</Select>
// Scenario 4: Select supplier in product form
// Call suppliers table to provide supplier options
const supplierOptions = await client.models.dataset_e1f2a3b4c5d6789012345678abcdef78.getSelectOptions({
code: "supplier_id",
label: "supplier_name",
});
// Used in: <Select>Select Supplier</Select>
Remember the core principle: Which table's data, call that table's getSelectOptions()
getSelectOptions() operation is only supported in WebAPI mode (Cookie authentication). OpenAPI mode does not currently support this feature.
Execute Custom SQL v1.1.19+â
Execute custom SQL queries configured on the Lovrabet platform, suitable for complex statistics and aggregation query scenarios.
From v1.1.19 onwards, it is recommended to use client.sql.execute() instead of the old client.api.executeSql(). The old API is still available for backward compatibility.
// Recommended: Use client.sql namespace
const data = await client.sql.execute({
sqlCode: "fc8e7777-06e3847d"
});
// Check execution result
if (data.execSuccess && data.execResult) {
console.log(`Query successful, returned ${data.execResult.length} records`);
data.execResult.forEach((row) => {
console.log(row);
});
} else {
console.error("SQL execution failed");
}
// Parameterized query (prevent SQL injection)
const data = await client.sql.execute({
sqlCode: "fc8e7777-xxxxx",
params: {
userId: "123",
startDate: "2025-01-01",
endDate: "2025-12-31",
}
});
// Or use direct parameter passing (compatible)
const data = await client.sql.execute("fc8e7777-xxxxx", {
userId: "123",
startDate: "2025-01-01",
});
// Alias method (backward compatible, not recommended)
const data = await client.api.executeSql("fc8e7777-xxxxx", {
userId: "123",
});
Call BFF Backend Functions v1.3.0+â
Call BFF (Backend For Frontend) backend functions configured on the Lovrabet platform.
// Recommended: Use client.bff namespace
const result = await client.bff.execute({
scriptName: 'calculatePrice',
params: { productId: '123', quantity: 10 }
});
// With type hints
interface PriceResult {
originalPrice: number;
discountedPrice: number;
discount: number;
}
const price = await client.bff.execute<PriceResult>({
scriptName: 'calculatePrice',
params: { productId: '123', quantity: 10 }
});
console.log(`Original price: ${price.originalPrice}, Discounted price: ${price.discountedPrice}`);
// Alias method (backward compatible, not recommended)
const result = await client.api.bff('calculatePrice', {
productId: '123',
quantity: 10
});
SQL API and BFF API support complete TypeScript types, error handling, and other advanced features. See:
đ Advanced Query Featuresâ
Pagination Queryâ
// Basic pagination
const page1 = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter({
currentPage: 1,
pageSize: 10,
});
// Complete loading for large datasets
const loadAllData = async () => {
let allData = [];
let currentPage = 1;
let hasMore = true;
while (hasMore) {
const response = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter({
currentPage,
pageSize: 100, // Use larger page size for efficiency
});
allData.push(...response.tableData);
hasMore = response.tableData.length === 100;
currentPage++;
// Avoid infinite loop
if (currentPage > 1000) break;
}
return allData;
};
Conditional Queryâ
// Multi-condition query
const activeUsers = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter({
status: "active",
role: "admin",
createdAfter: "2024-01-01",
currentPage: 1,
pageSize: 50,
});
// Keyword search
const searchResults = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter({
keyword: "john", // Search for records containing "john"
pageSize: 20,
});
Sort Query v1.1.16+â
Use sortList parameter for single or multi-field sorting:
// Single field sort
const sortedUsers = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter(
{ currentPage: 1, pageSize: 20 },
[{ createTime: "desc" }] // Sort by creation time descending
);
// Multi-field sort
const complexSort = await client.models.dataset_f7e6d5c4b3a2901234567890fedcba98.filter(
{ currentPage: 1, pageSize: 50 },
[
{ priority: "desc" }, // First priority: sort by priority descending
{ createTime: "desc" }, // Second priority: sort by creation time descending
{ name: "asc" }, // Third priority: sort by name ascending
]
);
Advanced Filter Query (filter API) v1.1.22+â
The filter() method provides more powerful and flexible query capabilities than getList(), supporting complex condition combinations, range queries, fuzzy matching, and other advanced features.
Starting from v1.1.22, filter API supports both OpenAPI mode and WebAPI mode.
// Range query: Find users aged between 18-35
const youngUsers = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter({
where: {
age: {
$gte: 18, // Greater than or equal to 18
$lte: 35, // Less than or equal to 35
},
},
pageSize: 50,
});
// Set matching: Find orders with specific statuses
const pendingOrders = await client.models.dataset_a1b2c3d4e5f6789012345678abcdef12.filter({
where: {
status: { $in: ['pending', 'processing', 'confirmed'] },
},
sortList: [{ createTime: 'desc' }],
currentPage: 1,
pageSize: 20,
});
// Fuzzy search: Search users whose username contains specific keyword
const searchUsers = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter({
where: {
username: { $contain: 'john' },
email: { $endWith: '@example.com' },
},
fields: ['id', 'username', 'email', 'createTime'],
});
// Complex condition combination: Use $and and $or
const complexQuery = await client.models.dataset_f7e6d5c4b3a2901234567890fedcba98.filter({
where: {
$and: [
{ price: { $gte: 100, $lte: 500 } },
{
$or: [
{ category: { $eq: 'electronics' } },
{ tags: { $contain: 'featured' } },
],
},
],
},
sortList: [{ price: 'asc' }],
pageSize: 30,
});
Advantages of filter over getList:
- đ¯ Supports range queries (
$gte,$lte) - đ Supports fuzzy matching (
$contain,$startWith,$endWith) - đ§Ž Supports set operations (
$in,$ne) - đ Supports complex logic combinations (
$and,$or) - đ Supports field filtering (
fields) to reduce data transmission
For detailed operator descriptions and more examples, please refer to Filter API Complete Guide.
Complex Query Examplesâ
// Combined query: Get recently registered active users
const recentActiveUsers = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter(
{
status: "active",
registeredAfter: new Date(
Date.now() - 30 * 24 * 60 * 60 * 1000
).toISOString(), // Within 30 days
currentPage: 1,
pageSize: 20,
},
[{ registeredAt: "desc" }] // Sort by registration time descending
);
// Get user statistics by specific role
const getUsersByRole = async (role: string) => {
const response = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter({
role,
pageSize: 1, // Only need total count
});
return {
role,
count: response.total,
users: response.tableData,
};
};
đ Batch Operationsâ
Batch Createâ
// Batch create users
const createBatchUsers = async (
users: Array<{ name: string; email: string }>
) => {
const results = [];
// Process in batches to avoid single request being too large
const batchSize = 10;
for (let i = 0; i < users.length; i += batchSize) {
const batch = users.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map((user) => client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.create(user))
);
results.push(...batchResults);
// Avoid requests being too frequent
if (i + batchSize < users.length) {
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
return results;
};
Batch Updateâ
// Batch update user status
const batchUpdateStatus = async (userIds: string[], status: string) => {
const updates = userIds.map((id) =>
client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.update(id, { status })
);
try {
const results = await Promise.all(updates);
console.log(`Successfully updated ${results.length} users`);
return results;
} catch (error) {
console.error("Batch update failed:", error);
throw error;
}
};
đ Related Data Queryâ
Get Related Dataâ
// Get user and their orders
const getUserWithOrders = async (userId: string) => {
// Get user information
const user = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.getOne(userId);
// Get user's orders
const orders = await client.models.dataset_a1b2c3d4e5f6789012345678abcdef12.filter({
userId: userId,
currentPage: 1,
pageSize: 50,
});
return {
user,
orders: orders.tableData,
totalOrders: orders.total,
};
};
// Get order and customer information
const getOrderWithCustomer = async (orderId: string) => {
const order = await client.models.dataset_a1b2c3d4e5f6789012345678abcdef12.getOne(orderId);
const customer = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.getOne(order.customerId);
return {
...order,
customer,
};
};
Aggregation Query Simulationâ
// User order statistics
const getUserOrderStats = async (userId: string) => {
const orders = await client.models.dataset_a1b2c3d4e5f6789012345678abcdef12.filter({
userId,
pageSize: 1000, // Get all orders
});
const stats = {
totalOrders: orders.total,
totalAmount: 0,
averageAmount: 0,
statusBreakdown: {} as Record<string, number>,
};
// Calculate statistics
orders.tableData.forEach((order) => {
stats.totalAmount += order.amount || 0;
stats.statusBreakdown[order.status] =
(stats.statusBreakdown[order.status] || 0) + 1;
});
stats.averageAmount =
stats.totalOrders > 0 ? stats.totalAmount / stats.totalOrders : 0;
return stats;
};
â ī¸ Error Handlingâ
Basic Error Handlingâ
try {
const users = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter();
console.log("Successfully fetched users:", users);
} catch (error) {
console.error("Failed to fetch users:", error);
// Handle based on error type
if (error.status === 404) {
console.log("Resource not found");
} else if (error.status === 403) {
console.log("Insufficient permissions");
} else if (error.status === 500) {
console.log("Server error");
}
}
Using safe Function (No try-catch)v1.3.0+â
import { safe } from "@lovrabet/sdk";
// Concise error handling
const { data, error } = await safe(() =>
client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter()
);
if (error) {
console.error("Failed to fetch users:", error.message, error.description);
return;
}
console.log("Successfully fetched users:", data);
Concurrent request error handling:
const [usersResult, ordersResult] = await Promise.all([
safe(() => client.models.users.filter()),
safe(() => client.models.orders.filter()),
]);
if (usersResult.error) {
console.error("Failed to load users");
}
if (ordersResult.error) {
console.error("Failed to load orders");
}
// Use data
console.log("Users:", usersResult.data);
console.log("Orders:", ordersResult.data);
Advanced Error Handlingâ
// Request wrapper with retry
const withRetry = async <T>(
operation: () => Promise<T>,
maxRetries: number = 3,
delay: number = 1000
): Promise<T> => {
let lastError: any;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error: any) {
lastError = error;
// Some errors don't need retry
if (
error.status === 400 ||
error.status === 401 ||
error.status === 403
) {
throw error;
}
if (attempt < maxRetries) {
console.log(`Request failed, retrying after ${delay}ms (${attempt}/${maxRetries})`);
await new Promise((resolve) => setTimeout(resolve, delay));
delay *= 2; // Exponential backoff
}
}
}
throw lastError;
};
// Use retry wrapper
const robustGetUsers = () => withRetry(() => client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter());
Request Status Managementâ
// Request status management class
class RequestManager {
private loadingStates = new Map<string, boolean>();
private errors = new Map<string, any>();
async executeRequest<T>(
key: string,
operation: () => Promise<T>
): Promise<T> {
this.loadingStates.set(key, true);
this.errors.delete(key);
try {
const result = await operation();
return result;
} catch (error) {
this.errors.set(key, error);
throw error;
} finally {
this.loadingStates.set(key, false);
}
}
isLoading(key: string): boolean {
return this.loadingStates.get(key) || false;
}
getError(key: string): any {
return this.errors.get(key);
}
}
// Usage example
const requestManager = new RequestManager();
const loadUsers = () =>
requestManager.executeRequest("users_list", () =>
client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter()
);
đ¯ Performance Optimizationâ
Request Cachingâ
// Simple in-memory cache
class SimpleCache {
private cache = new Map<string, { data: any; timestamp: number }>();
private ttl = 5 * 60 * 1000; // 5 minute TTL
set(key: string, data: any) {
this.cache.set(key, { data, timestamp: Date.now() });
}
get(key: string): any | null {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() - item.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
return item.data;
}
clear() {
this.cache.clear();
}
}
const cache = new SimpleCache();
// Query function with caching
const getCachedUsers = async (params: any = {}) => {
const cacheKey = `users_${JSON.stringify(params)}`;
// Try to get from cache
let users = cache.get(cacheKey);
if (users) {
console.log("Returning data from cache");
return users;
}
// Cache miss, get from API
users = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter(params);
cache.set(cacheKey, users);
console.log("Fetched data from API and cached");
return users;
};
Request Deduplicationâ
// Request deduplication and merging
class RequestDeduplicator {
private pendingRequests = new Map<string, Promise<any>>();
async request<T>(key: string, operation: () => Promise<T>): Promise<T> {
// If the same request is in progress, return that Promise directly
if (this.pendingRequests.has(key)) {
return this.pendingRequests.get(key);
}
// Create new request
const promise = operation().finally(() => {
// Clear cache after request completes
this.pendingRequests.delete(key);
});
this.pendingRequests.set(key, promise);
return promise;
}
}
const deduplicator = new RequestDeduplicator();
// Use deduplicator
const getUsers = (params: any) =>
deduplicator.request(`users_${JSON.stringify(params)}`, () =>
client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter(params)
);
// Multiple calls with same parameters will be merged
Promise.all([
getUsers({ status: "active" }),
getUsers({ status: "active" }),
getUsers({ status: "active" }),
]); // Actually only makes one request
Pagination Optimizationâ
// Progressive loading
class InfiniteLoader {
private data: any[] = [];
private currentPage = 1;
private hasMore = true;
private loading = false;
async loadMore(pageSize: number = 20): Promise<any[]> {
if (this.loading || !this.hasMore) {
return this.data;
}
this.loading = true;
try {
const response = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter({
currentPage: this.currentPage,
pageSize,
});
this.data.push(...response.tableData);
this.hasMore = response.tableData.length === pageSize;
this.currentPage++;
return this.data;
} finally {
this.loading = false;
}
}
reset() {
this.data = [];
this.currentPage = 1;
this.hasMore = true;
this.loading = false;
}
getData() {
return this.data;
}
isLoading() {
return this.loading;
}
canLoadMore() {
return this.hasMore && !this.loading;
}
}
đ Data Processing Toolsâ
Data Transformationâ
// Data transformation utility
const transformUserData = (rawUser: any) => ({
id: rawUser.id,
name: rawUser.name,
email: rawUser.email,
displayName: `${rawUser.name} (${rawUser.email})`,
isActive: rawUser.status === "active",
createdAt: new Date(rawUser.created_at),
avatar: rawUser.avatar_url || "/default-avatar.png",
});
// Batch transformation
const transformUserList = (response: any) => ({
users: response.tableData.map(transformUserData),
pagination: {
current: response.currentPage,
size: response.pageSize,
total: response.total,
pages: Math.ceil(response.total / response.pageSize),
},
});
// Usage
const getUsersForUI = async () => {
const response = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter();
return transformUserList(response);
};
Data Validationâ
// Data validation utility
const validateUserData = (userData: any) => {
const errors: string[] = [];
if (!userData.name || userData.name.trim().length === 0) {
errors.push("Name cannot be empty");
}
if (!userData.email || !/\S+@\S+\.\S+/.test(userData.email)) {
errors.push("Please enter a valid email address");
}
if (userData.age && (userData.age < 0 || userData.age > 120)) {
errors.push("Age must be between 0-120");
}
return {
isValid: errors.length === 0,
errors,
};
};
// Safe user creation
const createUserSafely = async (userData: any) => {
const validation = validateUserData(userData);
if (!validation.isValid) {
throw new Error(`Data validation failed: ${validation.errors.join(", ")}`);
}
return await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.create(userData);
};
đ Next Stepsâ
After mastering API usage, you can continue learning:
- đ¯ TypeScript Support - Type-safe development experience
- đ Advanced Features - Multi-project support and performance optimization
- đ ī¸ Practical Examples - React/Vue integration examples
â Frequently Asked Questionsâ
Q: How to handle large data volume queries?â
It's recommended to use pagination queries to avoid loading too much data at once:
// Recommended: Pagination query
const response = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter({
currentPage: 1,
pageSize: 50, // Adjust according to actual needs
});
// Not recommended: Get all data
// const allUsers = await loadAllData(); // May cause performance issues
Q: How to debug when API calls fail?â
Enable detailed error logging:
const client = createClient({
options: {
onError: (error) => {
console.group("API Error Details");
console.log("Message:", error.message);
console.log("Status:", error.status);
console.log("URL:", error.url);
console.log("Request Data:", error.requestData);
console.log("Response Data:", error.responseData);
console.groupEnd();
},
},
});
Q: How to implement optimistic updates?â
// Optimistic update example
const optimisticUpdate = async (userId: string, updates: any) => {
// 1. Update UI immediately (optimistic update)
updateUserInUI(userId, updates);
try {
// 2. Send API request
const updatedUser = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.update(userId, updates);
// 3. Update UI with server response data
updateUserInUI(userId, updatedUser);
return updatedUser;
} catch (error) {
// 4. If failed, rollback UI state
revertUserInUI(userId);
throw error;
}
};