Development Guidelines & Common Pitfalls
This document summarizes proven best practices for integration development on the Lovrabet platform.
If you're not yet familiar with Lovrabet's tool ecosystem, we recommend reading Developer Toolchain Overview first.
AI-Driven Development Guidelines (Recommended)
The template says "Use singleton pattern for API client" — without a backend, you don't even have an API, let alone an API client. You'd need to build the backend first, write endpoints, handle authentication, handle errors, then write a request wrapper. "Add pagination to list queries" — the backend pagination endpoint hasn't been written yet, so you have to ask the backend developer to add it, and aligning parameter names takes another half day. "Handle errors with try-catch" — each backend developer returns errors in a different format: one returns { code: 500, msg: "xxx" }, another returns { success: false, error: "xxx" }, and when you catch the error you still don't know how to present it to the user. The template describes best practices, but without backend APIs, every rule is building castles in the air. With rabetbase: CLI's api pull already generates a standard SDK client at src/api/client.ts, filter parameters and return values have TypeScript types, and exception types are defined in the SDK. Skill ensures AI has exclusive access to this real information — every guideline in the template is backed by actual types and methods. Not just conceptually correct platitudes, but an SOP supported by real infrastructure.
When developing with rabetbase CLI in Claude Code, the following prompt template ensures AI follows best practices:
Use rabetbase CLI to help me implement a {feature description}. Requirements:
- SDK client uses singleton pattern (
src/api/client.ts)- AccessKey must not be hardcoded; use environment variables or Cookie authentication
- List queries must include pagination, search must use debounce
- Error handling with try-catch, provide user-friendly error messages
- Sensitive data (phone numbers, ID numbers) must not be displayed in plain text on the frontend
AI will generate code following these guidelines. Below are detailed explanations of each practice, for your understanding and ongoing maintenance.
Application Scenarios
Before diving into specific practices, here are a few typical integration development scenarios to help you find the pattern closest to your needs.
Mini Program Ecosystem Expansion
Build WeChat Mini Programs and Alipay Mini Programs based on enterprise data:
import { createClient } from "@lovrabet/sdk";
const client = createClient({
appCode: "your-enterprise-app",
token: wx.getStorageSync("userToken"),
});
const customerData = await client.models.customers.filter({
region: "East China",
level: "VIP",
});
Workflow Automation
Build complex approval processes and data processing pipelines:
const approvalWorkflow = {
trigger: await client.models.orders.filter({ status: "pending" }),
conditions: [
{ field: "amount", operator: ">", value: 10000 },
{ field: "customer.level", operator: "=", value: "VIP" },
],
actions: ["sendNotification", "createApprovalTask", "updateOrderStatus"],
};
Multi-System Integration Hub
Connect Lovrabet with existing enterprise systems (ERP, CRM, OA):
const dataHub = {
lovrabet: await lovrabetClient.models.products.filter(),
erp: await erpConnector.getInventory(),
crm: await crmConnector.getCustomers(),
synchronize: () => {
// Complex data sync and transformation logic
},
};
Client Management
Frontend: Singleton Pattern Reuse
Create the client instance once and reuse it globally. Do not call createClient in every component.
// src/api/client.ts — Recommended: Singleton pattern
import { createClient } from "@lovrabet/sdk";
export const client = createClient({
appCode: "your-app-code",
});
// Use in components
import { client } from "@/api/client";
// Not recommended: Creating new instance on every render
function MyComponent() {
const client = createClient({ appCode: "..." }); // Creates new instance on every render
}
Backend: Spring Bean Singleton
@Configuration
public class LovrabetConfig {
@Bean
public LovrabetSDKClient lovrabetSDKClient() {
return new LovrabetSDKClient(accessKey, baseUrl);
}
}
Security
AccessKey Management
AccessKey is the credential for accessing data. Keep it secure.
# Pass via environment variable, do not hardcode in source code
export LOVRABET_ACCESS_KEY="ak-your-access-key"
// Never hardcode AccessKey in frontend
const client = createClient({
accessKey: "ak-xxx", // Dangerous! Will be exposed in browser
});
// Use Cookie or Token authentication in the browser
const client = createClient({
appCode: "your-app-code",
// Automatically uses Cookie authentication
});
Sensitive Data Masking
// Frontend: Mask phone number
const maskPhone = (phone: string) => {
return phone.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
};
// 138****8000
// Backend: Log masking
String maskedPhone = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
log.info("Customer phone: {}", maskedPhone);
Performance Optimization
Pagination and Debouncing
Control data volume in API calls, and add debounce for search scenarios:
// Use pagination, do not fetch all data at once
const { data } = await client.models.customers.filter({
currentPage: 1,
pageSize: 20,
});
// Add debounce to search, avoid sending requests on every keystroke
import { debounce } from "lodash-es";
const handleSearch = debounce(async (keyword) => {
await client.models.customers.filter({ searchKeyword: keyword });
}, 300);
Code Splitting
For projects with many pages, use lazy loading to reduce the initial bundle size:
import { lazy } from "react";
const CustomerAnalysis = lazy(() => import("./pages/CustomerAnalysis"));
Backend Caching
Add a caching layer for frequently read data:
private final Cache<String, Object> cache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
Error Handling
Frontend Unified Handling
try {
const result = await client.models.customers.filter(params);
if (result.success) {
setData(result.data);
} else {
message.error(result.message);
}
} catch (error) {
console.error("API call failed:", error);
message.error("Network error, please try again later");
}
Backend Retry Mechanism
For unstable network scenarios, add exponential backoff retry:
public <T> LovrabetResult<T> executeWithRetry(
Supplier<LovrabetResult<T>> operation, int maxRetries) {
for (int attempt = 0; attempt <= maxRetries; attempt++) {
try {
LovrabetResult<T> result = operation.get();
if (result.isSuccess()) return result;
Thread.sleep((long) Math.pow(2, attempt) * 1000);
} catch (Exception e) {
if (attempt == maxRetries) throw new RuntimeException("Retry failed", e);
}
}
return null;
}
Logging Standards
Backend services should use structured logging for easier troubleshooting:
@Slf4j
public class DataService {
public void createData(Map<String, Object> data) {
long startTime = System.currentTimeMillis();
log.info("Start operation - operation={}, dataSize={}", "create", data.size());
LovrabetResult<String> result = sdkClient.create(request);
long duration = System.currentTimeMillis() - startTime;
if (result.isSuccess()) {
log.info("Operation success - id={}, duration={}ms", result.getData(), duration);
} else {
log.error("Operation failed - code={}, duration={}ms", result.getResultCode(), duration);
}
}
}
Testing Strategy
Frontend Unit Testing
import { render, screen, waitFor } from "@testing-library/react";
test("should load customers", async () => {
render(<CustomerList />);
await waitFor(() => {
expect(screen.getByText("Zhang San")).toBeInTheDocument();
});
});
Backend Integration Testing
@SpringBootTest
public class DataServiceIntegrationTest {
@Autowired
private DataService dataService;
@Test
public void testGetCustomers() {
List<Map<String, Object>> customers = dataService.getCustomers(1, 10);
assertNotNull(customers);
assertTrue(customers.size() > 0);
}
}
Deployment Recommendations
Frontend Deployment
rabetbase run build
# Output in dist/ directory, deploy to CDN
Nginx configuration reference:
server {
listen 80;
root /var/www/your-app/dist;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Backend Docker Deployment
FROM openjdk:11-jre-slim
WORKDIR /app
COPY target/myapp.jar app.jar
ENV LOVRABET_ACCESS_KEY=""
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Health Check
@GetMapping("/health/lovrabet")
public Map<String, Object> checkHealth() {
Map<String, Object> health = new HashMap<>();
try {
long start = System.currentTimeMillis();
LovrabetResult<?> result = sdkClient.getDatasetCodeList(request);
health.put("status", result.isSuccess() ? "UP" : "DOWN");
health.put("responseTime", (System.currentTimeMillis() - start) + "ms");
} catch (Exception e) {
health.put("status", "DOWN");
health.put("error", e.getMessage());
}
return health;
}
Summary
| Practice Area | Key Points |
|---|---|
| Client Management | Use singleton pattern for both frontend and backend |
| Security | Never hardcode AccessKey in frontend |
| Performance | Pagination, caching, code splitting |
| Error Handling | Unified handling + retry mechanism |
| Logging | Structured logging, sensitive data masking |
| Testing | Unit tests + integration tests |
| Deployment | Health checks, containerization |
Related Documentation
- Developer Toolchain Overview — Learn about the five-tool ecosystem
- 5-Minute Quick Start — Complete process from scratch
- TypeScript SDK Guide — Frontend data operations
- Java SDK Guide — Backend data operations
- OpenAPI Documentation — HTTP API details