API Reference
This document provides a complete reference for all Lovrabet OpenAPI interfaces, including SDK usage and underlying HTTP specifications.
Basic Information
Environment URLs
| Environment | Domain |
|---|---|
| Production | https://runtime.lovrabet.com |
Request Specifications
- Request Method: All interfaces use
POST - Content Type:
application/json - Character Encoding:
UTF-8 - Timeout Setting: 30 seconds recommended
Authentication Method
All requests require the following authentication information in HTTP Headers:
| Header Name | Description | Example |
|---|---|---|
| X-Time-Stamp | Timestamp (milliseconds) | 1758903130713 |
| X-App-Code | Application code | app-c2dd52a2 |
| X-Dataset-Code | Dataset code (required for some APIs) | 0fefba76fe29...ff |
| X-Token | Signature Token | jdqqGtzecF2I6FIW... |
The SDK automatically adds all required headers without manual intervention.
OpenAPI Interface List
Complete Interface List
Below are all interfaces provided by Lovrabet OpenAPI with detailed information:
| Interface Name | HTTP Method | Full URL Path | SDK Method | Description |
|---|---|---|---|---|
| Batch query data | POST | /openapi/data/get-list | filter(params) | Paginated query with filtering and sorting |
| Query single data | POST | /openapi/data/get-one | getOne(id) | Get single data detail by ID |
| Create data | POST | /openapi/data/create | create(data) | Create new data record |
| Update data | POST | /openapi/data/update | update(id, data) | Update existing data record |
| Execute SQL | POST | /api/custom/executeSql | sql.execute(options) | Execute custom SQL queries |
OpenAPI currently does not support delete(), getSelectOptions(), and excelExport() operations. For these features, please use Cookie-based WebAPI mode.
Request Parameter Specifications
All OpenAPI request bodies follow this structure:
{
appCode: string; // Application code (required for all interfaces)
datasetCode: string; // Dataset code (required for all data operations)
paramMap: { // Business parameter object
// Specific parameters vary by interface
}
}
Authentication Information Delivery:
- OpenAPI authentication information is not in the request body, but passed through HTTP Headers:
X-Time-Stamp: TimestampX-App-Code: Application codeX-Dataset-Code: Dataset codeX-Token: Signature Token
- SDK automatically handles these headers, developers don't need to add them manually
Interface Parameter Details
1. Batch Query Data (getList)
URL: POST /openapi/data/get-list
Request Body Parameters:
{
appCode: string;
datasetCode: string;
paramMap: {
currentPage?: number; // Current page number, starts from 1
pageSize?: number; // Records per page
ytSortList?: SortList; // Sort configuration (auto-added via SDK sortList parameter)
[key: string]: any; // Other query conditions (based on dataset fields)
}
}
Parameter Description:
| Parameter Name | Type | Required | Default | Description |
|---|---|---|---|---|
appCode | string | ✅ Yes | - | Application code |
datasetCode | string | ✅ Yes | - | Dataset code |
paramMap.currentPage | number | ❌ No | 1 | Current page number, starts from 1 |
paramMap.pageSize | number | ❌ No | 20 | Records per page, max 100 |
paramMap.ytSortList | SortList | ❌ No | - | Sort configuration array, auto-generated from SDK sortList |
paramMap.[fieldName] | any | ❌ No | - | Other query conditions based on dataset field definitions |
Query Condition Examples:
Based on different dataset fields, you can use the following query patterns:
{
// Exact match
status: "active",
customer_type: "vip",
// Fuzzy search (field name with _like suffix)
name_like: "Zhang",
// Range query (field name with _start/_end suffix)
create_time_start: "2024-01-01",
create_time_end: "2024-12-31",
amount_start: 1000,
amount_end: 5000,
// IN query (field name with _in suffix, value as array)
status_in: ["active", "pending"],
// IS NULL query (field name with _is_null suffix)
deleted_at_is_null: true,
}
Sorting Feature (ytSortList):
OpenAPI uses the ytSortList field for sorting, passed through SDK's sortList parameter:
import { SortOrder } from "@lovrabet/sdk";
// SDK usage (recommended)
const result = await client.models.users.filter(
{ currentPage: 1, pageSize: 20 },
[
{ priority: SortOrder.DESC }, // Priority descending
{ createTime: SortOrder.DESC }, // Create time descending
{ name: SortOrder.ASC }, // Name ascending
]
);
// SDK automatically converts sortList to ytSortList field in paramMap
// Actual request body: { appCode, datasetCode, paramMap: { ytSortList: [...] } }
- OpenAPI only supports sorting through
ytSortListfield - When using SDK, pass sort configuration through the second parameter
sortListofgetList() - SDK automatically converts
sortListtoytSortListformat
Response Data (data field):
{
paging: {
pageSize: number; // Records per page
totalCount: number; // Total record count
currentPage: number; // Current page number
}
tableData: Array<T>; // Data list array
tableColumns: Array<{
title: string; // Column title
dataIndex: string; // Field name
}>;
}
2. Query Single Data (getOne)
URL: POST /openapi/data/get-one
Request Body Parameters:
{
appCode: string;
datasetCode: string;
paramMap: {
id: string | number; // Record ID
}
}
Parameter Description:
| Parameter Name | Type | Required | Description |
|---|---|---|---|
appCode | string | ✅ Yes | Application code |
datasetCode | string | ✅ Yes | Dataset code |
paramMap.id | string/number | ✅ Yes | Record ID to query |
Response Data (data field):
{
id: string | number; // Record ID
[key: string]: any; // Other fields based on dataset definition
}
3. Create Data (create)
URL: POST /openapi/data/create
Request Body Parameters:
{
appCode: string;
datasetCode: string;
paramMap: {
[key: string]: any; // Data fields to create
}
}
Parameter Description:
| Parameter Name | Type | Required | Description |
|---|---|---|---|
appCode | string | ✅ Yes | Application code |
datasetCode | string | ✅ Yes | Dataset code |
paramMap.[fieldName] | any | Based on field definition | Data to create, fields and requirements based on dataset definition |
Notes:
- System fields (such as
id,gmt_create,gmt_modified) are auto-generated, no need to provide - Required fields must be provided, otherwise parameter error will be returned
- Field types must match dataset definition
Response Data (data field):
{
id: string | number; // ID of newly created record
[key: string]: any; // Complete record data (including auto-generated fields)
}
4. Update Data (update)
URL: POST /openapi/data/update
Request Body Parameters:
{
appCode: string;
datasetCode: string;
paramMap: {
id: string | number; // Record ID to update
[key: string]: any; // Fields to update
}
}
Parameter Description:
| Parameter Name | Type | Required | Description |
|---|---|---|---|
appCode | string | ✅ Yes | Application code |
datasetCode | string | ✅ Yes | Dataset code |
paramMap.id | string/number | ✅ Yes | Record ID to update |
paramMap.[fieldName] | any | ❌ No | Fields to update, only need to provide fields to modify (partial update) |
Notes:
- Supports partial updates, only need to provide fields to modify
gmt_modifiedfield is automatically updated- Cannot modify
idandgmt_createfields
Response Data (data field):
{
id: string | number; // Record ID
[key: string]: any; // Complete updated record data
}
Response Structure
Common Response Format
All interface responses contain the following top-level fields (returned by platform gateway):
{
success: boolean; // Whether request succeeded
msg: string; // Response message
data: any; // Response data
errorCode?: number; // Error code (returned on failure)
errorMsg?: string; // Error message (returned on failure)
}
getList Response Fixed Fields
The data field of getList interface contains the following three fixed fields (SDK return structure is consistent with platform):
{
paging: {
// 【Fixed field】Pagination information
pageSize: number; // Records per page
totalCount: number; // Total record count
currentPage: number; // Current page number
}
tableData: Array<T>; // 【Fixed field】Data list array
tableColumns: Array<{
// 【Fixed field】Table column definition
title: string; // Column title
dataIndex: string; // Field name
}>;
}
SDK Usage
Create Client
import { createClient } from "@lovrabet/sdk";
const client = createClient({
appCode: "your-app-code",
accessKey: process.env.LOVRABET_ACCESS_KEY,
models: {
users: {
tableName: "users",
datasetCode: "your-dataset-code",
},
},
});
SDK Method List
Each model instance provides the following methods:
| Method | Description | Corresponding API |
|---|---|---|
getList(params?) | Batch query data | /openapi/data/get-list |
getOne(id) | Query single data | /openapi/data/get-one |
create(data) | Create single data | /openapi/data/create |
update(id, data) | Update single data | /openapi/data/update |
SQL Execution
OpenAPI mode supports executing custom SQL queries, suitable for complex reports, multi-table joins, and other scenarios.
Basic Usage
// Execute SQL query
const result = await client.sql.execute({
sqlCode: 'your-sql-code', // SQL code identifier
params: { // Optional: SQL parameters
startDate: '2024-01-01',
endDate: '2024-12-31'
}
});
// Check execution result
if (result.execSuccess) {
console.log('Query result:', result.execResult);
} else {
console.error('Execution failed:', result.execError);
}
Type-Safe Execution
interface MonthlyStats {
customer_name: string;
total_amount: number;
order_count: number;
}
const result = await client.sql.execute<MonthlyStats>({
sqlCode: 'monthly-sales-stats',
params: { year: 2024 }
});
if (result.execSuccess && result.execResult) {
result.execResult.forEach(stat => {
console.log(`${stat.customer_name}: $${stat.total_amount}`);
});
}
Safe Execution (Recommended)
Use the sqlSafe wrapper to simplify error handling:
import { sqlSafe } from "@lovrabet/sdk";
const { data, error } = await sqlSafe(() =>
client.sql.execute({
sqlCode: 'customer-orders',
params: { customerId: '123' }
})
);
if (error) {
console.error('Query failed:', error.message);
return;
}
// data is directly the query result array
console.log('Order count:', data.length);
Return Value Structure
interface SqlExecuteResult<T> {
execSuccess: boolean; // Whether SQL execution succeeded
execResult?: T[]; // Query result array (on success)
execError?: string; // Error message (on failure)
}
- Complex multi-table join queries
- Aggregation statistics reports
- Complex conditions not suitable for filter
- High-performance queries requiring optimization
Custom SQL needs to be pre-created and configured on the Lovrabet platform. You can manage SQL queries through MCP tools or the platform interface.
Detailed API Description
1. Batch Query Data (getList)
Paginated query of data in dataset, supports filtering, sorting, etc.
SDK Usage
Basic Query:
const response = await client.models.users.filter({
currentPage: 1,
pageSize: 20,
});
// Destructure response
const { paging, tableData, tableColumns } = response;
console.log("Total count:", paging.totalCount);
console.log("Current page:", paging.currentPage);
console.log("Data:", tableData);
console.log("Column definitions:", tableColumns);
Conditional Query:
const response = await client.models.users.filter({
currentPage: 1,
pageSize: 20,
// Query conditions (based on actual dataset fields)
status: "active",
customer_type: "vip",
});
Paginated Iteration:
async function getAllUsers() {
const allUsers = [];
let currentPage = 1;
const pageSize = 50;
while (true) {
const { paging, tableData } = await client.models.users.filter({
currentPage,
pageSize,
});
allUsers.push(...tableData);
// Check if there's more data
if (currentPage * pageSize >= paging.totalCount) {
break;
}
currentPage++;
// Avoid request flooding
await new Promise((resolve) => setTimeout(resolve, 100));
}
return allUsers;
}
HTTP Specifications
Interface URL: POST /openapi/data/get-list
Request Headers:
X-Time-Stamp: {timestamp}
X-App-Code: {appCode}
X-Dataset-Code: {datasetCode}
X-Token: {token}
Request Body:
{
"appCode": "app-c2dd52a2",
"datasetCode": "0fefba76fe29440194841f4825df53ff",
"paramMap": {
"pageSize": 10,
"currentPage": 1,
"status": "active",
"customer_type": "vip",
"create_time_start": "2024-01-01",
"create_time_end": "2024-12-31"
}
}
Request Body with Sorting Example:
{
"appCode": "app-c2dd52a2",
"datasetCode": "0fefba76fe29440194841f4825df53ff",
"paramMap": {
"pageSize": 10,
"currentPage": 1,
"ytSortList": [{ "gmt_create": "desc" }, { "id": "asc" }],
"status": "active"
}
}
Response Structure Description:
The data field contains three fixed fields:
paging- Pagination information (fixed field)tableData- Data list (fixed field)tableColumns- Table column definition (fixed field)
Response Example:
{
"success": true,
"msg": "Query successful",
"data": {
"paging": {
"pageSize": 10,
"totalCount": 156,
"currentPage": 1
},
"tableData": [
{
"id": "123",
"customer_name": "Example Customer",
"customer_type": "vip",
"contact_person": "Zhang San",
"phone": "13800138000",
"email": "example@example.com",
"address": "Chaoyang District, Beijing",
"status": "active",
"credit_level": "A",
"gmt_create": "2024-01-15 10:30:00",
"gmt_modified": "2024-03-20 14:25:00"
}
],
"tableColumns": [
{
"title": "Customer Name",
"dataIndex": "customer_name"
},
{
"title": "Customer Type",
"dataIndex": "customer_type"
},
{
"title": "Contact Person",
"dataIndex": "contact_person"
}
]
}
}
Query Parameter Details
Pagination Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
pageSize | number | No | 10 | Records per page, max 100 |
currentPage | number | No | 1 | Current page, starts from 1 |
Sorting Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ytSortList | Array | No | Sort configuration array, format: [{ "fieldName": "asc/desc" }] |
When using SDK, pass sort configuration through the second parameter sortList of getList(), SDK automatically converts to ytSortList field.
See "Sorting Feature" section above for details.
Conditional Query:
Query parameters vary by dataset fields, common query methods:
{
// Exact match
field_name: "value",
// Fuzzy search (some fields support)
field_name_like: "value",
// Range query
field_name_start: "value1",
field_name_end: "value2",
// IN query (some fields support)
field_name_in: ["value1", "value2"],
// NULL check (some fields support)
field_name_is_null: true,
}
2. Query Single Data (getOne)
Query single detailed data by unique identifier.
SDK Usage
// Query single data
const user = await client.models.users.getOne("user-id");
console.log(user);
// Returns single object containing all fields of the record
// Or use object parameter
const user = await client.models.users.getOne({
id: 14,
});
HTTP Specifications
Interface URL: POST /openapi/data/get-one
Request Headers:
X-Time-Stamp: {timestamp}
X-App-Code: {appCode}
X-Dataset-Code: {datasetCode}
X-Token: {token}
Request Body:
{
"appCode": "app-c2dd52a2",
"datasetCode": "0fefba76fe29440194841f4825df53ff",
"paramMap": {
"id": 14
}
}
Response Example:
{
"success": true,
"msg": "Query successful",
"data": {
"id": 14,
"customer_name": "Example Customer",
"customer_type": "vip",
"contact_person": "Zhang San",
"phone": "13800138000",
"email": "example@example.com",
"address": "XX Road XX, Chaoyang District, Beijing",
"status": "active",
"credit_level": "A",
"credit_amount": 100000.0,
"used_amount": 35000.0,
"available_amount": 65000.0,
"contract_start": "2024-01-01",
"contract_end": "2024-12-31",
"sales_person": "Manager Wang",
"department": "North China Sales Department",
"gmt_create": "2024-01-15 10:30:00",
"gmt_modified": "2024-03-20 14:25:00",
"remark": "Important VIP customer, requires priority maintenance"
}
}
3. Create Single Data (create)
Create a new data record.
SDK Usage
// Create customer information
const newCustomer = await client.models.users.create({
customer_name: "New Customer Company",
customer_type: "enterprise",
contact_person: "Manager Li",
phone: "13900139000",
email: "contact@newcustomer.com",
address: "Pudong New Area, Shanghai",
status: "active",
credit_level: "B",
});
console.log("New customer ID:", newCustomer.id);
console.log("Created time:", newCustomer.gmt_create);
HTTP Specifications
Interface URL: POST /openapi/data/create
Request Headers:
X-Time-Stamp: {timestamp}
X-App-Code: {appCode}
X-Dataset-Code: {datasetCode}
X-Token: {token}
Request Body:
{
"appCode": "app-c2dd52a2",
"datasetCode": "0fefba76fe29440194841f4825df53ff",
"paramMap": {
"customer_name": "New Customer Company",
"customer_type": "enterprise",
"contact_person": "Manager Li",
"phone": "13900139000",
"email": "contact@newcustomer.com",
"address": "Pudong New Area, Shanghai",
"status": "active",
"credit_level": "B"
}
}
Response Example:
{
"success": true,
"msg": "Created successfully",
"data": {
"id": 158,
"customer_name": "New Customer Company",
"customer_type": "enterprise",
"contact_person": "Manager Li",
"phone": "13900139000",
"email": "contact@newcustomer.com",
"address": "Pudong New Area, Shanghai",
"status": "active",
"credit_level": "B",
"credit_amount": 0.0,
"used_amount": 0.0,
"available_amount": 0.0,
"gmt_create": "2025-10-10 15:30:00",
"gmt_modified": "2025-10-10 15:30:00"
}
}
4. Update Single Data (update)
Update partial or all fields of an existing data record by ID.
SDK Usage
// Update customer information
const updatedCustomer = await client.models.users.update(158, {
customer_type: "vip",
credit_level: "A",
credit_amount: 200000.0,
remark: "Upgraded to VIP customer, increased credit limit",
});
console.log("Updated customer type:", updatedCustomer.customer_type);
console.log("Update time:", updatedCustomer.gmt_modified);
Batch Update Example:
// Batch update multiple customer statuses
const customerIds = [101, 102, 103];
const updateResults = await Promise.all(
customerIds.map((id) =>
client.models.users.update(id, {
status: "inactive",
remark: "Batch deactivation",
})
)
);
console.log(`Successfully updated ${updateResults.length} customers`);
HTTP Specifications
Interface URL: POST /openapi/data/update
Request Headers:
X-Time-Stamp: {timestamp}
X-App-Code: {appCode}
X-Dataset-Code: {datasetCode}
X-Token: {token}
Request Body:
{
"appCode": "app-c2dd52a2",
"datasetCode": "0fefba76fe29440194841f4825df53ff",
"paramMap": {
"id": 158,
"customer_type": "vip",
"credit_level": "A",
"credit_amount": 200000.0,
"remark": "Upgraded to VIP customer, increased credit limit"
}
}
Response Example:
{
"success": true,
"msg": "Updated successfully",
"data": {
"id": 158,
"customer_name": "New Customer Company",
"customer_type": "vip",
"contact_person": "Manager Li",
"phone": "13900139000",
"email": "contact@newcustomer.com",
"address": "Pudong New Area, Shanghai",
"status": "active",
"credit_level": "A",
"credit_amount": 200000.0,
"used_amount": 0.0,
"available_amount": 200000.0,
"gmt_create": "2025-10-10 15:30:00",
"gmt_modified": "2025-10-10 16:45:00",
"remark": "Upgraded to VIP customer, increased credit limit"
}
}
OpenAPI currently does not provide physical deletion interface. If you need to delete data, it's recommended to use "soft delete" approach by updating a status field via update() method:
// Recommended: Use soft delete (update status to deleted)
await client.models.users.update(158, {
status: "deleted",
deleted_at: new Date().toISOString(),
remark: "Deleted per customer request",
});
console.log("Customer marked as deleted");
Advantages of soft delete:
- ✅ Data is recoverable
- ✅ Maintains operation records
- ✅ Complies with data compliance requirements
Error Handling
Error Code Description
When a request fails, the success field is false, and corresponding error code and message are returned.
| Error Code | Description | Solution |
|---|---|---|
| 1001 | Parameter error | Check if request parameters are complete and formatted correctly |
| 1002 | Signature verification failed | Check Access Key, signature algorithm, timestamp |
| 1003 | Timestamp expired | Token expired (10 minutes), regenerate |
| 1004 | Application not found | Check if App Code is correct |
| 1005 | Dataset not found | Check if Dataset Code is correct |
| 1006 | No access permission | Confirm if application has access to dataset |
| 2001 | Query timeout | Optimize query conditions, reduce data volume |
| 2002 | Data not found | Check if query conditions or ID is correct |
| 3001 | Internal server error | Contact technical support |
| 4001 | Request rate exceeded | Reduce request frequency, implement rate limiting |
Error Response Example
{
"success": false,
"msg": null,
"data": null,
"errorCode": 1002,
"errorMsg": "Signature verification failed, please check Token generation algorithm"
}
SDK Error Handling
import { LovrabetError } from "@lovrabet/sdk";
try {
const users = await client.models.users.filter();
} catch (error) {
if (error instanceof LovrabetError) {
console.error("Error code:", error.statusCode);
console.error("Error message:", error.message);
console.error("Error details:", error.details);
// Handle based on error code
switch (error.statusCode) {
case 1002:
console.error("Signature verification failed, check Access Key");
break;
case 1003:
console.error("Token expired, need to refresh");
break;
case 1006:
console.error("No permission to access this dataset");
break;
default:
console.error("Other error");
}
} else {
console.error("Unknown error:", error);
}
}
Advanced Usage
1. TypeScript Type Support
import { createClient, ListResponse } from "@lovrabet/sdk";
// Define data type
interface User {
id: string;
name: string;
email: string;
status: "active" | "inactive";
gmt_create: string;
}
const client = createClient({
// ... configuration
});
// Typed query
const response: ListResponse<User> = await client.models.users.filter();
response.tableData.forEach((user: User) => {
console.log(user.name, user.email);
});
2. Multi-Model Configuration
const client = createClient({
appCode: "your-app-code",
accessKey: process.env.LOVRABET_ACCESS_KEY,
models: {
users: {
tableName: "users",
datasetCode: "dataset-001",
},
orders: {
tableName: "orders",
datasetCode: "dataset-002",
},
products: {
tableName: "products",
datasetCode: "dataset-003",
},
},
});
// Use different models
const users = await client.models.users.filter();
const orders = await client.models.orders.filter();
const products = await client.models.products.filter();
3. Response Data Transformation
const response = await client.models.users.filter();
// Extract pure data array
const users = response.tableData;
// Extract column definitions (for dynamic table UI construction)
const columns = response.tableColumns.map((col) => ({
title: col.title,
field: col.dataIndex,
}));
// Pagination information
const { totalCount, currentPage, pageSize } = response.paging;
const totalPages = Math.ceil(totalCount / pageSize);
4. Request Interception and Logging
import { createClient } from "@lovrabet/sdk";
const client = createClient({
appCode: "your-app-code",
accessKey: process.env.LOVRABET_ACCESS_KEY,
models: {
/* ... */
},
});
// Wrap request method to add logging
const originalGetList = client.models.users.getList.bind(client.models.users);
client.models.users.getList = async function (params) {
console.log("Request parameters:", params);
const startTime = Date.now();
try {
const result = await originalGetList(params);
console.log("Request successful, time:", Date.now() - startTime, "ms");
return result;
} catch (error) {
console.error("Request failed:", error);
throw error;
}
};
Performance Optimization
1. Pagination Query Recommendations
// ✅ Recommended: Reasonable page size
const response = await client.models.users.filter({
pageSize: 20, // 10-50 records is appropriate
});
// ❌ Avoid: Querying too much data at once
const response = await client.models.users.filter({
pageSize: 1000, // Not recommended, affects performance
});
2. Avoid N+1 Queries
// ❌ Not recommended: Loop calling getOne
for (const id of userIds) {
const user = await client.models.users.getOne(id);
// Process user
}
// ✅ Recommended: Use getList for batch query
const users = await client.models.users.filter({
id_in: userIds, // Assuming IN query is supported
});
3. Implement Caching
class CachedApiClient {
private cache = new Map<string, { data: any; timestamp: number }>();
private cacheTTL = 60000; // 1 minute
async getListCached(params: any) {
const cacheKey = JSON.stringify(params);
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheTTL) {
return cached.data;
}
const data = await client.models.users.filter(params);
this.cache.set(cacheKey, { data, timestamp: Date.now() });
return data;
}
}
4. Concurrency Control
// Limit concurrency
async function batchQuery(ids: string[], concurrency = 3) {
const results = [];
for (let i = 0; i < ids.length; i += concurrency) {
const batch = ids.slice(i, i + concurrency);
const batchResults = await Promise.all(
batch.map((id) => client.models.users.getOne(id))
);
results.push(...batchResults);
}
return results;
}
Request Limits
Rate Limits
| Limit Type | Limit Value |
|---|---|
| Per minute | 600 requests |
| Per hour | 10,000 requests |
| Per day | 100,000 requests |
Data Volume Limits
| Limit Type | Limit Value |
|---|---|
| Max records per single query | 100 records |
| Max response body size | 10MB |
| Max request body size | 1MB |
Concurrency Limits
| Limit Type | Limit Value |
|---|---|
| Max concurrent connections per app | 10 |
- Use connection pools to manage requests
- Implement request queues and rate limiting mechanisms
- Set reasonable timeout values
Raw HTTP Requests (Advanced)
Using SDK is recommended. The following content is for developers who need low-level implementation.
Signature Generation
import crypto from "crypto";
function generateToken(
timestamp: number,
appCode: string,
datasetCode: string,
accessKey: string,
secretKey: string = "lovrabet"
): string {
const params: Record<string, string> = {
accessKey: accessKey,
timeStamp: timestamp.toString(),
appCode: appCode,
};
if (datasetCode) {
params.datasetCode = datasetCode;
}
// Sort parameters by dictionary order
const sortedParams = Object.keys(params)
.sort()
.map((key) => `${key}=${params[key]}`)
.join("&");
// Calculate HMAC-SHA256
return crypto
.createHmac("sha256", secretKey)
.update(sortedParams, "utf8")
.digest("base64");
}
HTTP Request Example
async function getListRaw() {
const timestamp = Date.now();
const appCode = "app-c2dd52a2";
const datasetCode = "0fefba76fe29440194841f4825df53ff";
const accessKey = "your-access-key";
const token = generateToken(timestamp, appCode, datasetCode, accessKey);
const response = await fetch(
"https://runtime.lovrabet.com/openapi/data/get-list",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Time-Stamp": timestamp.toString(),
"X-App-Code": appCode,
"X-Dataset-Code": datasetCode,
"X-Token": token,
},
body: JSON.stringify({
appCode: appCode,
datasetCode: datasetCode,
paramMap: {
pageSize: 10,
currentPage: 1,
},
}),
}
);
return await response.json();
}
Best Practices
1. Error Handling and Retry
async function apiCallWithRetry(
fn: () => Promise<any>,
maxRetries = 3,
delay = 1000
) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error instanceof LovrabetError) {
// Token expiration can be retried
if (error.statusCode === 1003 && i < maxRetries - 1) {
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
}
throw error;
}
}
}
// Usage
const users = await apiCallWithRetry(() => client.models.users.filter());
2. Logging
function logApiCall(
method: string,
params: any,
result: any,
duration: number
) {
console.log({
timestamp: new Date().toISOString(),
method: method,
params: params,
success: true,
duration: duration,
recordCount: result.tableData?.length || 0,
});
}
3. Monitoring and Alerting
class ApiMonitor {
private errorCount = 0;
private errorThreshold = 10;
async callWithMonitoring(fn: () => Promise<any>) {
try {
const result = await fn();
this.errorCount = 0; // Reset error count
return result;
} catch (error) {
this.errorCount++;
if (this.errorCount >= this.errorThreshold) {
// Trigger alert
this.sendAlert("API error rate too high");
}
throw error;
}
}
private sendAlert(message: string) {
// Send alert notification
console.error("Alert:", message);
}
}
Related Documentation
- Authentication Guide - Learn about authentication mechanisms and Token management
- Quick Start - Get started quickly with examples
- Introduction - Learn about OpenAPI overview
Need Help?
If you encounter issues:
- Check error code descriptions in this document
- Check GitHub Issues
- Contact your business manager for technical support