Skip to main content

Development Guidelines & Common Pitfalls

This document summarizes proven best practices for integration development on the Lovrabet platform.

Prerequisite Reading

If you're not yet familiar with Lovrabet's tool ecosystem, we recommend reading Developer Toolchain Overview first.


Why can the prompt template be accurately executed by AI?

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 AreaKey Points
Client ManagementUse singleton pattern for both frontend and backend
SecurityNever hardcode AccessKey in frontend
PerformancePagination, caching, code splitting
Error HandlingUnified handling + retry mechanism
LoggingStructured logging, sensitive data masking
TestingUnit tests + integration tests
DeploymentHealth checks, containerization