Skip to main content

Quick Start

This guide will help you complete your first OpenAPI call in 5 minutes. We recommend using the official SDK, which automatically handles authentication, signing, and other complex logic.

Recommended to Use SDK

The official SDK has encapsulated all underlying implementations and is ready to use out of the box. If you need to understand the underlying implementation, please see the "Low-Level Implementation" section at the end of this document.

Prerequisites

Before starting, please ensure:

  • ✅ You have obtained OpenAPI access (contact your account manager)
  • ✅ You have obtained the following credentials:
    • App Code - Application code
    • Access Key - Access key
    • Dataset Code - Dataset code
  • ✅ You have installed Node.js 16+ or Bun

Install SDK

npm install @lovrabet/sdk
# or
bun add @lovrabet/sdk

Scenario 1: Server-Side Usage (Node.js/SSR)

Suitable for Node.js server-side, Next.js SSR, API routes, and similar scenarios.

Basic Usage

import { createClient } from "@lovrabet/sdk";

// Create client (using accessKey)
const client = createClient({
appCode: "your-app-code",
accessKey: process.env.LOVRABET_ACCESS_KEY, // Read from environment variable
models: {
users: {
tableName: "users",
datasetCode: "your-dataset-code",
},
},
});

// Query data list
const response = await client.models.users.filter({
currentPage: 1,
pageSize: 20,
});

console.log("Total count:", response.paging.totalCount);
console.log("Data:", response.tableData);

// Query single record
const user = await client.models.users.getOne("user-id");
console.log("User details:", user);

Next.js App Router Example

Using in Next.js server components:

// app/users/page.tsx (Server Component)
import { createClient } from "@lovrabet/sdk";

const client = createClient({
appCode: process.env.LOVRABET_APP_CODE!,
accessKey: process.env.LOVRABET_ACCESS_KEY!,
models: {
users: {
tableName: "users",
datasetCode: process.env.LOVRABET_DATASET_CODE!,
},
},
});

export default async function UsersPage() {
// Call directly in server component
const { tableData: users } = await client.models.users.filter({
pageSize: 10,
currentPage: 1,
});

return (
<div>
<h1>User List</h1>
{users.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}

Next.js API Route Example

Create an API route for frontend calls:

// app/api/users/route.ts
import { createClient } from "@lovrabet/sdk";
import { NextResponse } from "next/server";

const client = createClient({
appCode: process.env.LOVRABET_APP_CODE!,
accessKey: process.env.LOVRABET_ACCESS_KEY!,
models: {
users: {
tableName: "users",
datasetCode: process.env.LOVRABET_DATASET_CODE!,
},
},
});

export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get("page") || "1");
const size = parseInt(searchParams.get("size") || "20");

const data = await client.models.users.filter({
currentPage: page,
pageSize: size,
});

return NextResponse.json(data);
} catch (error) {
return NextResponse.json({ error: "Query failed" }, { status: 500 });
}
}

Scenario 2: Browser-Side Usage (Pre-generated Token)

Suitable for browser-side public data access, unauthenticated users, and similar scenarios.

Step 1: Server-Side Token Generation

Create an API route to generate tokens:

// app/api/token/route.ts
import { generateOpenApiToken } from "@lovrabet/sdk";
import { NextResponse } from "next/server";

export async function GET() {
try {
const result = await generateOpenApiToken({
appCode: process.env.LOVRABET_APP_CODE!,
datasetCode: process.env.LOVRABET_DATASET_CODE!,
accessKey: process.env.LOVRABET_ACCESS_KEY!,
});

// Return token, timestamp, and expiresAt
return NextResponse.json(result);
} catch (error) {
return NextResponse.json({ error: "Token generation failed" }, { status: 500 });
}
}

Step 2: Browser Token Usage

// app/users/client-page.tsx
"use client";

import { createClient } from "@lovrabet/sdk";
import { useEffect, useState } from "react";

export default function ClientUsersPage() {
const [users, setUsers] = useState([]);
const [client, setClient] = useState(null);

useEffect(() => {
async function init() {
// 1. Get token
const { token, timestamp } = await fetch("/api/token").then((r) =>
r.json()
);

// 2. Create client
const newClient = createClient({
appCode: "your-app-code",
token: token,
timestamp: timestamp,
models: {
users: {
tableName: "users",
datasetCode: "your-dataset-code",
},
},
});

setClient(newClient);

// 3. Query data
const { tableData } = await newClient.models.users.filter({
pageSize: 10,
});
setUsers(tableData);
}

init();
}, []);

return (
<div>
<h1>User List</h1>
{users.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}

Automatic Token Refresh

Tokens expire after 10 minutes and need to be refreshed periodically:

"use client";

import { createClient, isTokenExpiring } from "@lovrabet/sdk";
import { useEffect, useState } from "react";

export default function UsersWithRefresh() {
const [client, setClient] = useState(null);
const [timestamp, setTimestamp] = useState(null);

// Initialize client
useEffect(() => {
fetchTokenAndCreateClient();
}, []);

// Token expiration check
useEffect(() => {
if (!timestamp) return;

const interval = setInterval(() => {
// Check if token is about to expire (refresh 1 minute early)
if (isTokenExpiring(timestamp, 60000)) {
console.log("Token about to expire, refreshing...");
fetchTokenAndCreateClient();
}
}, 30000); // Check every 30 seconds

return () => clearInterval(interval);
}, [timestamp]);

async function fetchTokenAndCreateClient() {
const { token, timestamp: newTimestamp } = await fetch("/api/token").then(
(r) => r.json()
);

const newClient = createClient({
appCode: "your-app-code",
token: token,
timestamp: newTimestamp,
models: {
users: {
tableName: "users",
datasetCode: "your-dataset-code",
},
users: {
tableName: "users",
datasetCode: "your-dataset-code",
},
},
});

setClient(newClient);
setTimestamp(newTimestamp);
}

// ... other code
}

Suitable for authenticated users accessing private data.

"use client";

import { createClient } from "@lovrabet/sdk";
import { useEffect, useState } from "react";

export default function PrivateDataPage() {
const [data, setData] = useState([]);

useEffect(() => {
async function fetchData() {
// No authentication information needed, automatically uses browser cookies
const client = createClient({
appCode: "your-app-code",
models: {
orders: {
tableName: "orders",
datasetCode: "your-dataset-code",
},
},
});

// Request automatically carries user's login cookie
const { tableData } = await client.models.orders.filter();
setData(tableData);
}

fetchData();
}, []);

return (
<div>
<h1>My Orders</h1>
{data.map((order) => (
<div key={order.id}>{order.orderNo}</div>
))}
</div>
);
}

Multi-Model Configuration

Configure multiple data models at once:

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();

Advanced Queries

Paginated Queries

const response = await client.models.users.filter({
currentPage: 2,
pageSize: 50,
});

console.log("Current page:", response.paging.currentPage);
console.log("Page size:", response.paging.pageSize);
console.log("Total count:", response.paging.totalCount);
console.log("Data:", response.tableData);

Conditional Filtering

const response = await client.models.users.filter({
currentPage: 1,
pageSize: 20,
// Other query conditions are passed to paramMap
status: "active",
role: "admin",
});

Get All Data (Paginated Iteration)

async function getAllUsers() {
const allUsers = [];
let currentPage = 1;
const pageSize = 50;
let hasMore = true;

while (hasMore) {
const response = await client.models.users.filter({
currentPage,
pageSize,
});

allUsers.push(...response.tableData);

// Check if there's more data
hasMore = currentPage * pageSize < response.paging.totalCount;
currentPage++;

// Avoid requesting too fast
await new Promise((resolve) => setTimeout(resolve, 100));
}

return allUsers;
}

const allUsers = await getAllUsers();
console.log(`Retrieved ${allUsers.length} records in total`);

Error Handling

import { createClient, LovrabetError } from "@lovrabet/sdk";

try {
const response = await client.models.users.filter();
console.log(response.tableData);
} catch (error) {
if (error instanceof LovrabetError) {
console.error("API error:", error.message);
console.error("Status code:", error.statusCode);
console.error("Error details:", error.details);
} else {
console.error("Unknown error:", error);
}
}

Common Errors and Solutions

1. Signature Verification Failed

Error Message: errorCode: 1002, errorMsg: "Signature verification failed"

Solutions:

  • Check if Access Key is correct
  • Ensure using environment variables instead of hardcoding
  • Verify if appCode and datasetCode match

2. Token Expired

Error Message: errorCode: 1003, errorMsg: "Timestamp expired"

Solutions:

  • Ensure client time is accurate
  • Implement automatic token refresh mechanism on browser side
  • Use isTokenExpiring() to check token validity

3. No Access Permission

Error Message: errorCode: 1006, errorMsg: "No access permission"

Solutions:

  • Confirm if Dataset Code is correct
  • Check if application has access to this dataset
  • Contact account manager to confirm permission configuration

Environment Configuration

Using Environment Variables

Create .env.local file:

LOVRABET_APP_CODE=your-app-code
LOVRABET_ACCESS_KEY=your-access-key
LOVRABET_DATASET_CODE=your-dataset-code

Use in code:

const client = createClient({
appCode: process.env.LOVRABET_APP_CODE!,
accessKey: process.env.LOVRABET_ACCESS_KEY!,
models: {
users: {
tableName: "users",
datasetCode: process.env.LOVRABET_DATASET_CODE!,
},
},
});

Performance Optimization Suggestions

1. Use Reasonable Page Size

// ✅ Recommended: 10-50 records
const response = await client.models.users.filter({
pageSize: 20,
});

// ❌ Avoid: Querying too much data at once
const response = await client.models.users.filter({
pageSize: 1000, // Not recommended
});

2. Avoid Frequent getOne Calls

// ❌ Not recommended: Loop calling getOne
for (const id of userIds) {
const user = await client.models.users.getOne(id);
}

// ✅ Recommended: Use getList for batch queries
const users = await client.models.users.filter({
id_in: userIds, // Assuming IN query is supported
});

3. Implement Caching

const cache = new Map();

async function getCachedUsers() {
const cacheKey = "users-list";

if (cache.has(cacheKey)) {
const { data, timestamp } = cache.get(cacheKey);
// Cache for 1 minute
if (Date.now() - timestamp < 60000) {
return data;
}
}

const data = await client.models.users.filter();
cache.set(cacheKey, { data, timestamp: Date.now() });

return data;
}

Low-Level Implementation (Advanced)

For Reference Only

Below is the low-level implementation without using the SDK. Strongly recommend using the SDK unless you have special requirements.

Signature Algorithm

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");
}

Raw HTTP Request

import axios from "axios";

async function getListRaw() {
const timestamp = Date.now();
const appCode = "your-app-code";
const datasetCode = "your-dataset-id";
const accessKey = "your-access-key";

const token = generateToken(timestamp, appCode, datasetCode, accessKey);

const response = await axios.post(
"https://runtime.lovrabet.com/openapi/data/get-list",
{
appCode: appCode,
datasetCode: datasetCode,
paramMap: {
pageSize: 10,
currentPage: 1,
},
},
{
headers: {
"Content-Type": "application/json",
"X-Time-Stamp": timestamp.toString(),
"X-App-Code": appCode,
"X-Dataset-Code": datasetCode,
"X-Token": token,
},
}
);

return response.data;
}

Next Steps

Congratulations! You have mastered the basic usage of Lovrabet OpenAPI. Next:

  • Authentication Guide - Deep dive into the three authentication modes and token management
  • API Reference - View complete endpoint documentation and advanced features

Need Help?

If you encounter issues:

  1. Check the common errors section in this document
  2. Check the GitHub Repository Issues
  3. Contact your account manager for technical support