Skip to main content

Migrate from LeanCloud

This document helps you migrate your project from LeanCloud to CloudBase.

Migration Overview

Feature Comparison

LeanCloudCloudBaseDescription
Data StorageDocument DatabaseBoth are JSON document storage
Cloud EngineCloud Functions/CloudRunBackend code hosting
File ServiceCloud StorageFile storage service
User SystemIdentity AuthenticationUser authentication service
Instant Messaging-Need to implement yourself or use third-party services
Push Service-Need to use Tencent Cloud Push Service

Data Storage Migration

1. Export LeanCloud Data

Export data in LeanCloud Console:

  1. Go to "Data Storage" → "Data Management"
  2. Select the Class to export
  3. Click "Export", select JSONL format (one JSON object per line)
Export Format Description

LeanCloud exports JSONL files with one record per line, for example:

{"objectId": "6666e6b6b6666666bb66b66b", "createdAt": "2025-07-02T07:58:45.609Z", "updatedAt": "2025-07-02T07:58:53.087Z", "email": "user@example.com", "username": "testuser"}

2. Data Format Conversion

LeanCloud and CloudBase have different data formats and need conversion. We provide a one-click intelligent migration script:

  • ✅ Automatically convert to CloudBase format
  • ✅ Intelligently handle JSONL format (one object per line)
  • ✅ Support Pointer relationship conversion
  • ✅ Support GeoPoint geolocation conversion

Field Conversion Mapping Table

LeanCloud FieldCloudBase FieldDescription
objectId_idData unique ID
objectId_openidCloudBase user ID
objectIdleancloud_objectIdRetain original ID for data tracing
createdAt_createTimeISO 8601 → millisecond timestamp
updatedAt_updateTimeISO 8601 → millisecond timestamp
Pointer_ref_*Association reference marker (need manual handling)
GeoPoint{type, coordinates}GeoJSON format [longitude, latitude]
ACL(delete)CloudBase uses security rules instead
authDatauidExtract uid field, delete authData
Other fields(fully preserved)email, username, etc.

Using Migration Script

Step 1: Create directory structure

migration/
├── leancloud-export/ # Place LeanCloud exported JSONL files
├── cloudbase-import/ # Output directory (auto-created)
└── cloudbase-migrate-leancloud.cjs # Migration script

Step 2: Create migration script cloudbase-migrate-leancloud.cjs

#!/usr/bin/env node

/**
* LeanCloud → CloudBase Data Migration Tool
*
* Features:
* - Convert LeanCloud data format to CloudBase format
* - Map objectId → _id and _openid
* - Convert timestamps to millisecond format
* - Support Pointer and GeoPoint type conversion
*/

const fs = require('fs');
const path = require('path');

// Configuration
const CONFIG = {
inputDir: path.join(__dirname, 'leancloud-export'),
outputDir: path.join(__dirname, 'cloudbase-import'),
keepOriginalId: true,
excludeFields: ['ACL', '__type', 'className'],
};

// Convert ISO 8601 time to millisecond timestamp
function convertTimestamp(isoString) {
if (!isoString) return null;
try {
return new Date(isoString).getTime();
} catch (error) {
console.warn(`⚠️ Time conversion failed: ${isoString}`);
return null;
}
}

// Extract uid from authData
function extractAuthDataUid(authData) {
if (!authData || typeof authData !== 'object') return null;

for (const provider of Object.keys(authData)) {
const providerData = authData[provider];
if (providerData?.uid) return providerData.uid;
}
return null;
}

// Convert Pointer type
function convertPointer(pointer, fieldName) {
if (!pointer || pointer.__type !== 'Pointer') return pointer;

return {
_ref_className: pointer.className,
_ref_objectId: pointer.objectId,
_ref_note: `Need manual replacement with CloudBase _id (original field: ${fieldName})`
};
}

// Convert GeoPoint type to GeoJSON format
function convertGeoPoint(geoPoint) {
if (!geoPoint || geoPoint.__type !== 'GeoPoint') return geoPoint;

const { latitude, longitude } = geoPoint;
if (typeof latitude !== 'number' || typeof longitude !== 'number') {
console.warn(`⚠️ GeoPoint coordinates invalid`);
return geoPoint;
}

// CloudBase uses GeoJSON format: [longitude, latitude]
return {
type: 'Point',
coordinates: [longitude, latitude]
};
}

// Recursively convert special types
function convertSpecialTypes(obj, fieldName = 'root') {
if (!obj || typeof obj !== 'object') return obj;

if (obj.__type === 'Pointer') return convertPointer(obj, fieldName);
if (obj.__type === 'GeoPoint') return convertGeoPoint(obj);

if (Array.isArray(obj)) {
return obj.map((item, index) => convertSpecialTypes(item, `${fieldName}[${index}]`));
}

const result = {};
Object.keys(obj).forEach(key => {
result[key] = convertSpecialTypes(obj[key], key);
});
return result;
}

// Convert single record
function convertRecord(leancloudRecord) {
const cloudbaseRecord = {};

// Map objectId
if (leancloudRecord.objectId) {
cloudbaseRecord._id = leancloudRecord.objectId;
cloudbaseRecord._openid = leancloudRecord.objectId;
if (CONFIG.keepOriginalId) {
cloudbaseRecord.leancloud_objectId = leancloudRecord.objectId;
}
}

// Convert timestamps
if (leancloudRecord.createdAt) {
cloudbaseRecord._createTime = convertTimestamp(leancloudRecord.createdAt);
}
if (leancloudRecord.updatedAt) {
cloudbaseRecord._updateTime = convertTimestamp(leancloudRecord.updatedAt);
}

// Extract uid from authData
if (leancloudRecord.authData) {
const uid = extractAuthDataUid(leancloudRecord.authData);
if (uid) cloudbaseRecord.uid = uid;
}

// Copy other fields and convert special types
Object.keys(leancloudRecord).forEach(key => {
if (!['objectId', 'createdAt', 'updatedAt', 'authData', ...CONFIG.excludeFields].includes(key)) {
cloudbaseRecord[key] = convertSpecialTypes(leancloudRecord[key], key);
}
});

return cloudbaseRecord;
}

// Convert JSONL file
function convertFile(inputFile, outputFile) {
console.log(`\n📄 Converting file: ${path.basename(inputFile)}`);

const content = fs.readFileSync(inputFile, 'utf-8').trim();
if (!content) {
console.log(' ⚠️ File is empty, skipping');
return { records: 0 };
}

const lines = content.split(/\r?\n/).filter(line => line.trim());
console.log(` Found ${lines.length} lines of data`);

const cloudbaseRecords = [];
let pointerCount = 0, geoPointCount = 0;

lines.forEach((line, index) => {
try {
const record = convertRecord(JSON.parse(line));
const recordStr = JSON.stringify(record);
if (recordStr.includes('_ref_')) pointerCount++;
if (recordStr.includes('"type":"Point"')) geoPointCount++;
cloudbaseRecords.push(record);
} catch (error) {
console.log(` ⚠️ Line ${index + 1} parsing failed: ${error.message}`);
}
});

if (pointerCount > 0) console.log(` 🔗 Detected ${pointerCount} Pointer references`);
if (geoPointCount > 0) console.log(` 📍 Detected ${geoPointCount} GeoPoints`);

// Output JSONL format
const jsonlContent = cloudbaseRecords.map(r => JSON.stringify(r)).join('\n');
fs.writeFileSync(outputFile, jsonlContent, 'utf-8');

console.log(` ✅ Conversion successful: ${cloudbaseRecords.length} records`);
return { records: cloudbaseRecords.length };
}

// Main function
function main() {
console.log('🚀 Starting LeanCloud → CloudBase data migration\n');

if (!fs.existsSync(CONFIG.inputDir)) {
console.error(`❌ Input directory does not exist: ${CONFIG.inputDir}`);
return;
}

if (!fs.existsSync(CONFIG.outputDir)) {
fs.mkdirSync(CONFIG.outputDir, { recursive: true });
}

const files = fs.readdirSync(CONFIG.inputDir).filter(f => f.endsWith('.jsonl'));

if (files.length === 0) {
console.error('❌ No JSONL files found');
return;
}

console.log(`📁 Found ${files.length} files to process`);

let totalRecords = 0;
files.forEach(file => {
const inputFile = path.join(CONFIG.inputDir, file);
const outputFile = path.join(CONFIG.outputDir, file.replace('.jsonl', '.json'));
const result = convertFile(inputFile, outputFile);
totalRecords += result.records;
});

console.log('\n============================================================');
console.log(`🎉 Migration complete! Converted ${totalRecords} records`);
console.log(` Output directory: ${CONFIG.outputDir}`);
console.log('\n📌 Next step: Import converted files in CloudBase Console');
}

main();

Step 3: Run migration script

node cloudbase-migrate-leancloud.cjs

Output example:

🚀 Starting LeanCloud → CloudBase data migration

📁 Found 1 files to process

📄 Converting file: lc_user_masked.jsonl
Found 1162 lines of data
🔗 Detected 25 Pointer references
📍 Detected 10 GeoPoints
✅ Conversion successful: 1162 records

============================================================
🎉 Migration complete! Converted 1162 records
Output directory: cloudbase-import

📌 Next step: Import converted files in CloudBase Console

Conversion Example

LeanCloud original data:

{"objectId": "6666e6b6b6666666bb66b66b", "createdAt": "2025-07-02T07:58:45.609Z", "updatedAt": "2025-07-02T07:58:53.087Z", "email": "user@example.com", "username": "testuser"}

CloudBase converted:

{"_id":"6666e6b6b6666666bb66b66b","_openid":"6666e6b6b6666666bb66b66b","leancloud_objectId":"6666e6b6b6666666bb66b66b","_createTime":1751443125609,"_updateTime":1751443133087,"email":"user@example.com","username":"testuser"}

Pointer Relationship Conversion

LeanCloud original data:

{
"objectId": "post123",
"title": "Test Article",
"author": {
"__type": "Pointer",
"className": "_User",
"objectId": "user123"
}
}

CloudBase converted:

{
"_id": "post123",
"title": "Test Article",
"author": {
"_ref_className": "_User",
"_ref_objectId": "user123",
"_ref_note": "Need manual replacement with CloudBase _id (original field: author)"
}
}
Pointer Handling

Pointer references will be marked as _ref_* fields, after import need to manually or via script replace _ref_objectId with corresponding CloudBase _id.

GeoPoint Geolocation Conversion

LeanCloud original data:

{
"location": {
"__type": "GeoPoint",
"latitude": 22.5431,
"longitude": 114.0579
}
}

CloudBase converted (GeoJSON format):

{
"location": {
"type": "Point",
"coordinates": [114.0579, 22.5431]
}
}

GeoPoint will be automatically converted to GeoJSON format, coordinate order is [longitude, latitude], CloudBase will automatically recognize it on import.

3. Import to CloudBase

In the console:

  1. Login to CloudBase Console
  2. Go to "Document Database" → "Data Management"
  3. Create corresponding collections
  4. Click "Import" to upload converted JSON files
File Size Limit

Console import has a maximum limit of 50MB. If data files exceed this limit, please use the batch write script below.

4. Large Data Volume Batch Write

When data volume is large (exceeds 50MB), use script for batch write:

// batch-import.js
const cloudbase = require('@cloudbase/node-sdk');
const fs = require('fs');

const app = cloudbase.init({
env: 'your-env-id', // Replace with your environment ID
secretId: 'your-secret-id', // Replace with your SecretId
secretKey: 'your-secret-key', // Replace with your SecretKey
});

const db = app.database();
const BATCH_SIZE = 100; // Records per batch

async function batchImport(collectionName, jsonFile) {
const content = fs.readFileSync(jsonFile, 'utf-8').trim();
const lines = content.split(/\r?\n/).filter(line => line.trim());
const total = lines.length;
let imported = 0;

console.log(`Starting import of ${total} records to ${collectionName}...`);

for (let i = 0; i < total; i += BATCH_SIZE) {
const batch = lines.slice(i, i + BATCH_SIZE).map(line => JSON.parse(line));

const tasks = batch.map(item => db.collection(collectionName).add(item));
await Promise.all(tasks);

imported += batch.length;
console.log(`Progress: ${imported}/${total} (${(imported/total*100).toFixed(1)}%)`);
}

console.log(`Import complete! Total ${imported} records`);
}

// Usage example
batchImport('your-collection', 'cloudbase-import/your-file.json');

Install dependencies and run:

npm install @cloudbase/node-sdk
node batch-import.js
tip
  • SecretId and SecretKey can be obtained from Tencent Cloud Access Management
  • Recommend splitting large files into multiple small files for easier resumable upload

5. Configure Security Rules

Since CloudBase uses security rules to replace LeanCloud's ACL permissions, after importing data you need to configure security rules:

Example 1: Public read, only creator can write

{
"read": true,
"write": "doc._openid == auth.openid"
}

Example 2: Only logged-in users can read and write

{
"read": "auth != null",
"write": "auth != null"
}

Example 3: Role-based permissions

{
"read": "auth != null",
"write": "get('database.users.${auth.uid}').role == 'admin'"
}

For more security rule configurations, refer to Security Rules Documentation.

Cloud Engine Migration

LeanCloud Cloud Engine projects can be migrated to CloudBase CloudRun or Cloud Functions. We provide detailed migration guides, including:

  • leanengine.yaml configuration mapping: How to convert LeanCloud configuration to CloudRun deployment configuration
  • Dockerfile-free deployment: CloudRun supports automatic framework recognition, no need to write Dockerfile
  • Hook migration: How to implement hooks like beforeSave/afterSave through cloud function encapsulation
  • Scheduled task migration: How to migrate Cron tasks to cloud function scheduled triggers
  • Complete feature comparison table: Correspondence between LeanCloud API and CloudBase API
Complete Migration Guide

Please refer to LeanCloud Cloud Engine Migration to CloudRun/Cloud Functions Guide for detailed migration steps and code examples.

Quick Reference

LeanCloud Cloud Engine FeatureCloudBase Corresponding Solution
leanengine.yaml configurationCloudRun deployment configuration
AV.Cloud.define()Cloud Function exports.main
AV.Cloud.beforeSave()Cloud Function encapsulating data operations
AV.Cloud.afterSave()Cloud Function encapsulating data operations
Scheduled tasks (Cron)Scheduled triggers
Environment variablesCloudRun/Cloud Function environment variables

Environment Variable Migration

Configure environment variables in CloudRun or Cloud Function console to replace original LeanCloud environment variables:

LeanCloud Environment VariableCloudBase AlternativeDescription
LEANCLOUD_APP_IDENV_IDCloudBase environment ID
LEANCLOUD_APP_KEYNot neededCloudRun internal auto-authentication
LEANCLOUD_APP_MASTER_KEYNot neededCloudRun internal auto-authentication
LEANCLOUD_APP_PORTPORTService listening port
LEANCLOUD_APP_ENVTCB_ENVEnvironment identifier (customizable)

Custom environment variables originally configured in LeanCloud console need to be reconfigured in CloudRun or Cloud Function console.

Hook Migration Solution

CloudBase does not support database triggers, LeanCloud's beforeSave, afterSave and other Hooks need to be implemented through cloud function encapsulating data operations:

Migration Approach: Encapsulate database operations in cloud functions, clients operate data by calling cloud functions instead of directly operating the database.

LeanCloud beforeSave Hook:

// LeanCloud - Automatically process before save
AV.Cloud.beforeSave('Todo', async (request) => {
const todo = request.object;
if (!todo.get('title')) {
throw new AV.Cloud.Error('Title cannot be empty');
}
todo.set('status', 'pending');
});

CloudBase Cloud Function Encapsulation:

// CloudBase Cloud Function - functions/addTodo/index.js
const cloudbase = require('@cloudbase/node-sdk');

const app = cloudbase.init({ env: process.env.ENV_ID });
const db = app.database();

exports.main = async (event, context) => {
const { title, content } = event;

// beforeSave logic: validation and preprocessing
if (!title) {
return { success: false, error: 'Title cannot be empty' };
}

// Add data
const result = await db.collection('Todo').add({
title,
content,
status: 'pending', // Automatically set default value
_createTime: Date.now(),
});

// afterSave logic: subsequent processing (e.g., send notification)
// await sendNotification(result.id);

return { success: true, id: result.id };
};

Client call:

// Client operates data through cloud function, not directly operate database
const result = await app.callFunction({
name: 'addTodo',
data: { title: 'New Task', content: 'Task content' }
});
Migration Recommendations
  • Encapsulate all data operations requiring Hooks as cloud functions
  • Clients uniformly operate data through cloud functions
  • Can implement validation, default values, association operations, etc. in cloud functions

Simple Example

LeanCloud Cloud Function:

// LeanCloud
const AV = require('leanengine');

AV.Cloud.define('hello', async (request) => {
const { name } = request.params;
return { message: `Hello, ${name}!` };
});

CloudBase Cloud Function:

// CloudBase Cloud Function
exports.main = async (event, context) => {
const { name } = event;
return { message: `Hello, ${name}!` };
};

CloudBase CloudRun (Express):

// CloudBase CloudRun
const express = require('express');
const app = express();

app.use(express.json());

app.post('/hello', (req, res) => {
const { name } = req.body;
res.json({ message: `Hello, ${name}!` });
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});

File Storage Migration

We provide an official migration script tool to help you batch migrate files stored in LeanCloud to Tencent CloudBase Cloud Storage.

Download Migration Script

This migration solution requires using the official provided migration script tool. Please download the script package first:

➡️ Download LeanCloud Migration Tool

🚀 After downloading, extract to local, please carefully read the README.md document, and follow the steps below for configuration and execution.

Features

  • ✅ Read all file metadata from LeanCloud _File table
  • ✅ Automatically construct URLs and batch download files
  • ✅ Batch upload to Tencent CloudBase Cloud Storage
  • ✅ Support concurrency control to improve migration efficiency
  • ✅ Automatically retry failed downloads
  • ✅ Generate detailed migration report

Project Structure

Original Structure (after extraction)

This is the file structure you see after extracting the zip package:

leancloud_storage_migrate/
├── migrate.js # Main migration script (core file)
├── package.json # Project dependency configuration file
└── README.md # Project documentation
Description

These 3 files are the original project files, need manual configuration before use.

Structure after Installing Dependencies and Running Script

leancloud_storage_migrate/
├── migrate.js # Main migration script (core file)
├── package.json # Project dependency configuration file
├── package-lock.json # Dependency version lock file (auto-generated)
├── README.md # Project documentation
├── .gitignore # Git ignore file configuration
├── node_modules/ # Dependency package directory (generated after npm install)
├── temp_files/ # Temporary file storage directory (auto-created at runtime, auto-deleted after completion)
└── migration_report.json # Migration report file (generated after completion)

Quick Start

Step 1: Install Node.js Dependencies

Run the following command in the project directory to install required dependencies:

cd /path/to/leancloud_storage_migrate
npm install

This will automatically install the following dependency packages:

  • leancloud-storage - LeanCloud SDK
  • @cloudbase/node-sdk - Tencent CloudBase Node.js SDK

Step 2: Configure Parameters

Open the migrate.js file, find the configuration area (approximately lines 17-32), fill in your configuration information:

// LeanCloud Configuration
const LEANCLOUD_CONFIG = {
appId: 'YOUR_LEANCLOUD_APP_ID', // 👈 Replace with your LeanCloud App ID
appKey: 'YOUR_LEANCLOUD_APP_KEY', // 👈 Replace with your LeanCloud App Key
serverURL: 'https://YOUR_LEANCLOUD_SERVER_URL', // 👈 Replace with your API domain
fileDomain: 'https://YOUR_FILE_DOMAIN' // 👈 Replace with your file domain
};

// Tencent CloudBase Configuration
const CLOUDBASE_CONFIG = {
env: 'YOUR_ENV_ID', // Tencent CloudBase environment ID
secretId: 'YOUR_SECRET_ID', // Tencent Cloud API key SecretId
secretKey: 'YOUR_SECRET_KEY' // Tencent Cloud API key SecretKey
};
Get Configuration Information

LeanCloud Configuration:

  1. Login to LeanCloud Console
  2. Select your application
  3. Go to "Settings" → "App Credentials", get App ID and App Key
  4. Go to "Settings" → "Domain Binding", get API domain (serverURL) and file domain (fileDomain)

Tencent CloudBase Configuration:

  1. Login to Tencent CloudBase Platform
  2. Select your environment, get environment ID (env)
  3. Login to Tencent Cloud API Key Management
  4. Create or view key, get SecretId and SecretKey

⚠️ Important: Configure Security Domain

  • Login to Tencent CloudBase Platform
  • Go to "Environment Management" → "Security Sources"
  • Add your domain to the security domain list (avoid CORS errors)

Step 3: Run Migration Script

After configuration is complete, execute the following command to start migration:

npm start

Or run directly with Node.js:

node migrate.js

Step 4: View Results

The script will display progress information in real-time:

========================================
LeanCloud → Tencent CloudBase File Migration Tool
========================================

Starting to query LeanCloud _File table...
Queried 50 files...
✓ Total 50 files queried

Starting file migration, concurrency: 5
----------------------------------------

[1] Processing file: example.jpg
Download URL: https://xxx.com/xxx.jpg
Downloading...
✓ Download complete
Uploading to CloudBase: migrated/example.jpg
✓ Upload complete, fileID: cloud://xxx...

[2] Processing file: photo.png
Download URL: https://xxx.com/yyy.png
Downloading...
✓ Download complete
Uploading to CloudBase: migrated/photo.png
✓ Upload complete, fileID: cloud://yyy...

...

========================================
Migration Complete!
----------------------------------------

Total: 50 files
Success: 48
Failed: 2

Migration report saved to: ./migration_report.json

After completion, a migration_report.json report file will be generated in the current directory.

Optional Configuration

You can adjust the following configurations to optimize migration performance:

const DOWNLOAD_CONFIG = {
tempDir: './temp_files', // Temporary file storage directory
concurrency: 5, // Concurrent download count (recommend 3-10)
retryTimes: 3, // Download failure retry times
timeout: 30000 // Request timeout (milliseconds)
};

Migration Process

  1. Query file list: Query all files from LeanCloud _File table
  2. Download files: Construct URL based on key and download to local temporary directory
  3. Upload files: Upload files to Tencent CloudBase Cloud Storage
  4. Generate report: Generate migration_report.json report file
  5. Clean temporary files: Delete local temporary files

Output Results

After migration is complete, a migration_report.json report file will be generated, containing:

{
"timestamp": "2026-01-21T10:30:00.000Z",
"summary": {
"total": 100,
"success": 98,
"failed": 2
},
"successList": [
{
"objectId": "xxx",
"name": "example.jpg",
"fileID": "cloud://xxx.cloudbase.net/xxx",
"cloudPath": "migrated/example.jpg"
}
],
"errorList": [
{
"objectId": "yyy",
"name": "failed.png",
"error": "Download timeout"
}
]
}

Notes

  1. Network Stability: Ensure stable network connection, large file migration may take longer
  2. Disk Space: Ensure sufficient disk space to store temporary files
  3. Concurrency Count: Adjust concurrency based on network bandwidth, avoid too high causing request failures
  4. Retry Mechanism: Failed files will automatically retry, final failures will be recorded in the report
  5. Cloud Path: All files will be uploaded to migrated/ directory, can be modified as needed
  6. fileID Saving: Uploaded fileID will be recorded in the report, recommend saving for future use

Troubleshooting

Download Failed

  • Check if fileDomain configuration is correct
  • Check if network connection is normal
  • Try increasing timeout timeout

Upload Failed

  • Confirm Tencent CloudBase environment ID is correct
  • Check if security domain is configured
  • Confirm cloud storage quota is sufficient

CORS Error

  • Login to Tencent CloudBase Console
  • Add your domain in "Security Configuration" → "Security Sources"

User System Migration

This section helps you migrate LeanCloud user system (_User table) to CloudBase user system.

Migration Goals

  • Write LeanCloud objectId to CloudBase user table (using admin API's Uid field), achieving original ID remains unchanged
  • Synchronize user-related fields (e.g., name, nickname, phone, email, avatar, etc.)
  • After migration, reconfigure your business permission logic based on security rules (replace LeanCloud ACL)

Login Method Comparison

Migration solution heavily depends on your current login method:

  • If you use username/email/phone + password: Need to plan a transition plan for "password cannot be directly migrated" (verification code login priority / temporary password / dual-stack).
  • If you use email/phone + verification code: Migration is relatively simple, focus is on writing email/phone fields correctly and ensuring security rules and business permissions are correct.
Login MethodLeanCloudCloudBase
Username + Password✅ Built-in supportBuilt-in support
Email + Verification Code✅ Built-in supportBuilt-in support
Phone + Verification Code✅ Built-in supportBuilt-in support
WeChat Login✅ Third-party loginDeep support
UnionID Linking✅ Support✅ Support
QQ Login✅ Third-party login✅ OAuth support
Weibo Login✅ Third-party login✅ OAuth support
Apple Login✅ Built-in support🕐 Coming soon
Anonymous Login✅ Built-in support✅ Built-in support
Custom Login❌ Not supportedSupported

CloudBase and LeanCloud User System Differences

  • User Table Structure: CloudBase user system's built-in fields are a fixed set (e.g., Uid, Name, Phone, Email, etc.), recommend putting your business custom fields in a self-built collection (e.g., user_profile), linked by Uid.
  • Password Migration: LeanCloud usually does not allow exporting plain text passwords; even if you can get the hash, cannot directly write to CloudBase user system. You need to choose a "post-migration reset/reset" strategy (solutions provided below).
  • Username Validation Rule Differences:
    • CloudBase admin API allows Name to support special characters like @ when creating users (more lenient).
    • But "user self-registration" frontend/client validation rules may be stricter (e.g., not allowing @), the two are not completely consistent.

Migration Process

  1. Export LeanCloud user data (export from _User as JSON)
  2. Field mapping and cleaning (objectIdUid, handle correspondence of name/phone/email)
  3. Call CloudBase admin API to batch create users (CreateUser)
  4. Synchronize custom business fields (write to self-built collection, linked by Uid)
  5. Configure security rules and permission logic (replace LeanCloud ACL)
  6. Sample verification and rollback plan (DescribeUserList / DeleteUsers)

Prerequisites

  • CloudBase Environment: Prepare EnvId (environment ID).
  • Access Credentials: Prepare Tencent Cloud keys that can call CloudBase admin API (SecretId/SecretKey). Recommend:
    • Use sub-account + minimum permission policy
    • Inject keys through environment variables, avoid writing in code or documentation
  • API Documentation: CloudBase Identity Authentication / User Management API Documentation

Field Mapping

Taking LeanCloud _User common fields as example (your actual fields are based on export):

LeanCloud FieldDescriptionCloudBase FieldWrite Method
objectIdUser unique identifierUidRequired: Write to CreateUser.Uid, achieving "original ID remains unchanged"
name / usernameLogin username (may be username / phone / email address)NameRecommend priority writing to Name
mobilePhoneNumberPhone numberPhoneIf exists and compliant, write to Phone, after writing user can use phone verification code login
emailEmailEmailIf exists and compliant, write to Email, after writing user can use email verification code login
nicknameNicknameNickNameOptional
avatar / avatarUrlAvatar linkAvatarUrlOptional (need public network accessible)
Other business fieldsE.g., membership level, channel, invitation relationship, etc.Not recommend writing to user system tableRecommend writing to self-built collection user_profile, linked by Uid
Special Note on "Phone/Email + Password Login"

If in your LeanCloud business, users use phone + password or email + password login, and you store phone/email in name (or username), then when migrating to CloudBase:

  • Besides writing LeanCloud's name to CloudBase's Name, also need to synchronize LeanCloud's name to CloudBase's Phone and/or Email field, otherwise users will not be able to login in the original way.
  • Practical suggestions:
    • If name is like email (contains @ and meets email format) → Write Email = name
    • If name is like phone (11 digits, etc.) → Write Phone = name
    • If name is not phone/email, don't force write to Phone/Email, please use your original mobilePhoneNumber/email field, or guide users to change to verification code login.

Password Migration Strategy

Because original password cannot be migrated, usually three feasible solutions:

SolutionDescriptionApplicable Scenario
Solution 1: Verification Code Login PriorityAfter migration, guide users to login with SMS/email verification code, then reset password on "Set Password" pageRecommended, good user experience
Solution 2: Set Temporary PasswordGenerate temporary password for each user during import (script can generate random strong password), force change after first loginNeed to notify users of temporary password
Solution 3: Login State MigrationIf you have self-developed authentication/SSO, can short-term dual-stack: login goes to old system, business data gradually switches to CloudBase; finally unify password systemComplex scenario

Migration Script

We provide a one-click migration script with features including:

  • ✅ Read LeanCloud exported user JSON (array)
  • ✅ Call CloudBase admin API CreateUser to batch create users
  • ✅ Write objectId to Uid, keep original ID unchanged
  • ✅ If user already exists (returns duplicate data), automatically call ModifyUser to update fields (default does not reset password)
  • ✅ Optional: Mirror name to Phone/Email (for phone/email password login scenario)
  • ✅ Support multiple password modes: don't write password / unified default password / generate random temporary password for each user

1. Prepare LeanCloud Export File

Export _User table as JSON array format in LeanCloud console, example:

[
{
"objectId": "6864e6b5b9570274df85a79e",
"username": "sample@163.com",
"email": "sample@163.com",
"emailVerified": true,
"mobilePhoneVerified": false,
"nickname": "sample1",
"createdAt": "2025-07-02T07:58:45.609Z",
"updatedAt": "2025-07-02T07:58:53.087Z",
"ACL": { "*": { "read": true, "write": true } }
},
{
"objectId": "6870cf46bf6125518c3c7979",
"username": "88888888@qq.com",
"email": "88888888@qq.com",
"emailVerified": false,
"mobilePhoneVerified": false,
"nickname": "sample2",
"createdAt": "2025-07-11T08:45:58.532Z",
"updatedAt": "2025-07-11T08:45:58.532Z",
"ACL": { "*": { "read": true, "write": true } }
},
{
"objectId": "6893900f6f00df1bd11a0499",
"username": "4umxxxxfb0",
"emailVerified": false,
"mobilePhoneVerified": false,
"nickname": "sample3",
"createdAt": "2025-08-06T17:25:35.704Z",
"updatedAt": "2026-01-10T11:37:08.378Z",
"authData": {
"lc_apple": {
"uid": "001842.56ca44c39bf84bb4aaa377c87fe57613.1555",
"token_type": "Bearer",
"expires_in": 3600
}
},
"ACL": { "*": { "read": true, "write": true } }
}
]

2. Create Migration Script

Create migrate-leancloud-users-to-cloudbase.js file:

/*
* LeanCloud -> CloudBase User Migration Script Example
*
* Features:
* - Read LeanCloud exported user JSON (array)
* - Call CloudBase admin API CreateUser to batch create users
* - Write objectId to Uid, Name/Phone/Email/NickName etc. fields write by mapping
*
* Security Note:
* - Don't hardcode SecretId/SecretKey in code, please inject with environment variables
* - Production environment recommend using sub-account minimum permissions
*/

'use strict';

const fs = require('fs');
const https = require('https');
const crypto = require('crypto');

function mustGetEnv(name) {
const v = process.env[name];
if (!v) throw new Error(`Missing required env: ${name}`);
return v;
}

function getEnv(name, defaultValue) {
const v = process.env[name];
return v == null || v === '' ? defaultValue : v;
}

function sha256Hex(str) {
return crypto.createHash('sha256').update(str, 'utf8').digest('hex');
}

function hmacSha256(key, msg, output = 'buffer') {
return crypto.createHmac('sha256', key).update(msg, 'utf8').digest(output);
}

function toDateYmd(tsSeconds) {
const d = new Date(tsSeconds * 1000);
const yyyy = d.getUTCFullYear();
const mm = String(d.getUTCMonth() + 1).padStart(2, '0');
const dd = String(d.getUTCDate()).padStart(2, '0');
return `${yyyy}-${mm}-${dd}`;
}

function isLikelyEmail(s) {
if (!s) return false;
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(s).trim());
}

function isLikelyCnPhone(s) {
if (!s) return false;
return /^\d{11}$/.test(String(s).trim());
}

function normalizeUserType(v) {
const t = String(v || '').trim();
if (!t) return 'internalUser';
if (t === 'internalUser' || t === 'externalUser') return t;
throw new Error('USER_TYPE must be one of: internalUser | externalUser');
}

function generateStrongPassword(length = 16) {
const lower = 'abcdefghijklmnopqrstuvwxyz';
const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const digits = '0123456789';
const special = '()!@#$%^&*|?><_-';

const pick = (chars) => chars[crypto.randomInt(0, chars.length)];

const first = crypto.randomInt(0, 2) === 0 ? pick(lower + upper) : pick(digits);
const required = [pick(lower), pick(upper), pick(digits), pick(special)];

const all = lower + upper + digits + special;
const restLen = Math.max(8, Math.min(32, length)) - 1 - required.length;
const rest = Array.from({ length: restLen }, () => pick(all));

const arr = [...required, ...rest];
for (let i = arr.length - 1; i > 0; i--) {
const j = crypto.randomInt(0, i + 1);
[arr[i], arr[j]] = [arr[j], arr[i]];
}

return first + arr.join('');
}

function tc3BuildAuthHeader({
secretId,
secretKey,
service,
host,
action,
timestamp,
region,
payloadJson,
}) {
const algorithm = 'TC3-HMAC-SHA256';
const date = toDateYmd(timestamp);

const httpRequestMethod = 'POST';
const canonicalUri = '/';
const canonicalQuerystring = '';

const canonicalHeaders =
`content-type:application/json; charset=utf-8\n` +
`host:${host}\n`;

const signedHeaders = 'content-type;host';
const hashedRequestPayload = sha256Hex(payloadJson);

const canonicalRequest =
`${httpRequestMethod}\n` +
`${canonicalUri}\n` +
`${canonicalQuerystring}\n` +
`${canonicalHeaders}\n` +
`${signedHeaders}\n` +
`${hashedRequestPayload}`;

const credentialScope = `${date}/${service}/tc3_request`;
const stringToSign =
`${algorithm}\n` +
`${timestamp}\n` +
`${credentialScope}\n` +
`${sha256Hex(canonicalRequest)}`;

const secretDate = hmacSha256(`TC3${secretKey}`, date);
const secretService = hmacSha256(secretDate, service);
const secretSigning = hmacSha256(secretService, 'tc3_request');
const signature = hmacSha256(secretSigning, stringToSign, 'hex');

const authorization =
`${algorithm} ` +
`Credential=${secretId}/${credentialScope}, ` +
`SignedHeaders=${signedHeaders}, ` +
`Signature=${signature}`;

const headers = {
Authorization: authorization,
'Content-Type': 'application/json; charset=utf-8',
Host: host,
'X-TC-Action': action,
'X-TC-Timestamp': String(timestamp),
'X-TC-Version': '2018-06-08',
};

if (region) headers['X-TC-Region'] = region;

return headers;
}

function httpsJsonRequest({ host, path, headers, bodyJson }) {
return new Promise((resolve, reject) => {
const req = https.request(
{
hostname: host,
method: 'POST',
path,
headers,
},
(res) => {
let raw = '';
res.setEncoding('utf8');
res.on('data', (chunk) => (raw += chunk));
res.on('end', () => {
let parsed;
try {
parsed = JSON.parse(raw);
} catch (e) {
return reject(new Error(`Non-JSON response: ${raw.slice(0, 500)}`));
}

const resp = parsed && parsed.Response;
if (resp && resp.Error) {
const err = new Error(`${resp.Error.Code}: ${resp.Error.Message}`);
err.code = resp.Error.Code;
err.requestId = resp.RequestId;
err.httpStatus = res.statusCode;
return reject(err);
}

resolve({ statusCode: res.statusCode, data: parsed });
});
}
);

req.on('error', reject);
req.write(bodyJson);
req.end();
});
}

async function callTencentCloud({ action, payload }) {
const secretId = mustGetEnv('TENCENT_SECRET_ID');
const secretKey = mustGetEnv('TENCENT_SECRET_KEY');
const region = getEnv('TENCENT_REGION', '');
const host = getEnv('TENCENT_ENDPOINT', 'tcb.tencentcloudapi.com');

const timestamp = Math.floor(Date.now() / 1000);
const payloadJson = JSON.stringify(payload);

const headers = tc3BuildAuthHeader({
secretId,
secretKey,
service: 'tcb',
host,
action,
timestamp,
region,
payloadJson,
});

const resp = await httpsJsonRequest({
host,
path: '/',
headers,
bodyJson: payloadJson,
});

return resp.data;
}

function normalizeLeanCloudUser(u) {
const objectId = u.objectId || u.objectID || u.id;
const name = u.name || u.username || u.userName;

return {
objectId: objectId ? String(objectId) : '',
name: name ? String(name) : '',
phone: (u.mobilePhoneNumber || u.phone || u.mobile || '').toString(),
email: (u.email || '').toString(),
nickname: (u.nickname || u.nickName || '').toString(),
avatarUrl: (u.avatarUrl || u.avatar || '').toString(),
description: (u.description || '').toString(),
raw: u,
};
}

function mapToCloudBaseCreateUser({ envId, leanUser, userType, mirrorNameToLogin, setPasswordMode, defaultPassword }) {
if (!leanUser.objectId) throw new Error('Missing objectId');

const payload = {
EnvId: envId,
Uid: leanUser.objectId,
Name: leanUser.name || leanUser.objectId,
Type: userType,
UserStatus: 'ACTIVE',
};

if (leanUser.nickname) payload.NickName = leanUser.nickname;
if (leanUser.avatarUrl) payload.AvatarUrl = leanUser.avatarUrl;
if (leanUser.description) payload.Description = leanUser.description;

const trimmedName = (leanUser.name || '').trim();

if (leanUser.phone && isLikelyCnPhone(leanUser.phone)) {
payload.Phone = leanUser.phone.trim();
} else if (mirrorNameToLogin && isLikelyCnPhone(trimmedName)) {
payload.Phone = trimmedName;
}

if (leanUser.email && isLikelyEmail(leanUser.email)) {
payload.Email = leanUser.email.trim();
} else if (mirrorNameToLogin && isLikelyEmail(trimmedName)) {
payload.Email = trimmedName;
}

if (setPasswordMode === 'default') {
if (!defaultPassword) throw new Error('SET_PASSWORD_MODE=default requires DEFAULT_PASSWORD');
payload.Password = defaultPassword;
} else if (setPasswordMode === 'random') {
payload.Password = generateStrongPassword(16);
}

return payload;
}

function mapToCloudBaseModifyUser({ envId, leanUser, mirrorNameToLogin, updatePasswordOnDuplicate, setPasswordMode, defaultPassword }) {
if (!leanUser.objectId) throw new Error('Missing objectId');

const payload = {
EnvId: envId,
Uid: leanUser.objectId,
};

if (leanUser.name) payload.Name = leanUser.name;
if (leanUser.nickname) payload.NickName = leanUser.nickname;
if (leanUser.avatarUrl) payload.AvatarUrl = leanUser.avatarUrl;
if (leanUser.description) payload.Description = leanUser.description;

const trimmedName = (leanUser.name || '').trim();

if (leanUser.phone && isLikelyCnPhone(leanUser.phone)) {
payload.Phone = leanUser.phone.trim();
} else if (mirrorNameToLogin && isLikelyCnPhone(trimmedName)) {
payload.Phone = trimmedName;
}

if (leanUser.email && isLikelyEmail(leanUser.email)) {
payload.Email = leanUser.email.trim();
} else if (mirrorNameToLogin && isLikelyEmail(trimmedName)) {
payload.Email = trimmedName;
}

if (updatePasswordOnDuplicate) {
if (setPasswordMode === 'default') {
if (!defaultPassword) throw new Error('SET_PASSWORD_MODE=default requires DEFAULT_PASSWORD');
payload.Password = defaultPassword;
} else if (setPasswordMode === 'random') {
payload.Password = generateStrongPassword(16);
}
}

return payload;
}

function sleep(ms) {
return new Promise((r) => setTimeout(r, ms));
}

function isRetryableError(err) {
const code = err && (err.code || '');
if (!code) return false;
return [
'RequestLimitExceeded',
'InternalError',
'FailedOperation',
'ResourceUnavailable',
].includes(code);
}

async function withRetry(fn, { maxAttempts = 5, baseDelayMs = 300 } = {}) {
let attempt = 0;
while (true) {
attempt += 1;
try {
return await fn();
} catch (err) {
if (attempt >= maxAttempts || !isRetryableError(err)) throw err;
const delay = baseDelayMs * Math.pow(2, attempt - 1) + crypto.randomInt(0, 200);
await sleep(delay);
}
}
}

async function main() {
const envId = mustGetEnv('TENCENT_ENV_ID');
const inputFile = getEnv('INPUT_FILE', './leancloud_users.json');

const mirrorNameToLogin = String(getEnv('MIRROR_NAME_TO_LOGIN', 'false')).toLowerCase() === 'true';
const userType = normalizeUserType(getEnv('USER_TYPE', 'internalUser'));

const setPasswordMode = String(getEnv('SET_PASSWORD_MODE', 'none')).toLowerCase();
const defaultPassword = getEnv('DEFAULT_PASSWORD', '');

console.log(
`[config] envId=${envId} inputFile=${inputFile} userType=${userType} mirrorNameToLogin=${mirrorNameToLogin} passwordMode=${setPasswordMode}`
);
if (!['none', 'default', 'random'].includes(setPasswordMode)) {
throw new Error('SET_PASSWORD_MODE must be one of: none | default | random');
}

const updatePasswordOnDuplicate =
String(getEnv('UPDATE_PASSWORD_ON_DUPLICATE', 'false')).toLowerCase() === 'true';

const concurrency = Math.max(1, parseInt(getEnv('CONCURRENCY', '3'), 10));

const raw = fs.readFileSync(inputFile, 'utf8');
const users = JSON.parse(raw);
if (!Array.isArray(users)) throw new Error('Input JSON must be an array');

const normalized = users.map(normalizeLeanCloudUser);

let created = 0;
let updated = 0;
let failed = 0;
const failures = [];

let idx = 0;
async function worker() {
while (true) {
const cur = idx;
idx += 1;
if (cur >= normalized.length) return;

const leanUser = normalized[cur];
try {
const payload = mapToCloudBaseCreateUser({
envId,
leanUser,
userType,
mirrorNameToLogin,
setPasswordMode,
defaultPassword,
});

await withRetry(() => callTencentCloud({ action: 'CreateUser', payload }), {
maxAttempts: 5,
baseDelayMs: 300,
});

created += 1;
const done = created + updated + failed;
if (done % 50 === 0) {
console.log(
`[progress] created=${created}, updated=${updated}, failed=${failed}, total=${normalized.length}`
);
}
} catch (err) {
if (err && err.code === 'FailedOperation.DuplicatedData') {
try {
const modifyPayload = mapToCloudBaseModifyUser({
envId,
leanUser,
mirrorNameToLogin,
updatePasswordOnDuplicate,
setPasswordMode,
defaultPassword,
});

await withRetry(() => callTencentCloud({ action: 'ModifyUser', payload: modifyPayload }), {
maxAttempts: 5,
baseDelayMs: 300,
});

updated += 1;
continue;
} catch (modifyErr) {
err = modifyErr;
}
}

failed += 1;
failures.push({
objectId: leanUser.objectId,
name: leanUser.name,
message: err.message,
code: err.code,
requestId: err.requestId,
});
console.error(`[fail] objectId=${leanUser.objectId} code=${err.code || 'unknown'} msg=${err.message}`);
}
}
}

const workers = Array.from({ length: concurrency }, () => worker());
await Promise.all(workers);

console.log(
`[done] created=${created}, updated=${updated}, failed=${failed}, total=${normalized.length}`
);
if (failures.length) {
fs.writeFileSync('migrate-failures.json', JSON.stringify(failures, null, 2), 'utf8');
console.log('Wrote failures to migrate-failures.json');
}
}

main().catch((e) => {
console.error(e);
process.exit(1);
});

3. Set Environment Variables

Environment VariableDescriptionRequired
TENCENT_SECRET_IDTencent Cloud SecretId, Get here✅ Required
TENCENT_SECRET_KEYTencent Cloud SecretKey, Get here✅ Required
TENCENT_ENV_IDCloudBase environment ID✅ Required
INPUT_FILEImport file path (default ./leancloud_users.json)Optional
USER_TYPEUser type: internalUser (internal) / externalUser (external)Optional
TENCENT_REGIONRegion (e.g., ap-shanghai)Optional
TENCENT_ENDPOINTAPI endpoint (default tcb.tencentcloudapi.com)Optional
CONCURRENCYConcurrency (default 3)Optional
User Type Selection

For "business App real end users" (LeanCloud original users), should usually choose externalUser, avoid occupying environment's "organization member count" quota.

Password-related environment variables (choose one):

ModeEnvironment VariableDescription
Don't write password (default)SET_PASSWORD_MODE=noneUsers need to reset password after verification code login
Unified default passwordSET_PASSWORD_MODE=default + DEFAULT_PASSWORD=xxxNot recommended, only for test environment
Random temporary passwordSET_PASSWORD_MODE=randomRecommended, generate random strong password for each user

Duplicate import behavior:

  • Default: If CreateUser returns duplicate data, script will use ModifyUser to update fields, will not reset password
  • UPDATE_PASSWORD_ON_DUPLICATE=true: Allow updating password according to SET_PASSWORD_MODE on duplicate import (generally not recommended)

Login name mirroring:

  • MIRROR_NAME_TO_LOGIN=true: When name looks like phone/email, automatically sync to Phone/Email

4. Run Script

# Set environment variables
export TENCENT_SECRET_ID="your-secret-id"
export TENCENT_SECRET_KEY="your-secret-key"
export TENCENT_ENV_ID="your-env-id"
export USER_TYPE="externalUser"
export INPUT_FILE="./leancloud_users.json"

# Run migration script
node migrate-leancloud-users-to-cloudbase.js

Output example:

[config] envId=your-env-id inputFile=./leancloud_users.json userType=externalUser mirrorNameToLogin=false passwordMode=none
[progress] created=50, updated=0, failed=0, total=1162
[progress] created=100, updated=0, failed=0, total=1162
...
[done] created=1162, updated=0, failed=0, total=1162

Script will output success/failure statistics, and record failed entries to local migrate-failures.json.

Verification and Rollback

Verification:

  • Sample call DescribeUserList on CloudBase side to query if corresponding Uid/Name/Phone/Email exists
  • Business side initiate login verification (verification code login/password login), need to first enable corresponding login capabilities in Console / Identity Authentication / Login Methods

Rollback:

  • If need to delete users imported this time, can call DeleteUsers, maximum 100 Uid at a time.

Permission Migration

After migrating users, you need to apply original LeanCloud's ACL/role permissions to CloudBase's security rules and business authentication.

Recommended approach:

  • Use auth.uid (user ID in login state) as root of permission judgment
  • Set rules like "only owner can read/write" "only admin can write" for each collection
  • Complex permissions (organizational structure, roles, resource authorization) recommend maintaining roles/permissions in database, security rules do basic backup, core authentication in cloud functions/server side

Refer to Security Rules Documentation

User Migration FAQ

Q: Can I migrate LeanCloud passwords directly?

A: Usually not possible. Recommend using verification code login transition, or set temporary password and guide users to change on first login.

Q: Will @ in name cause creation failure?

A: When creating users with admin API, Name supports @ (different from user self-registration validation). You should still ensure Email/Phone fields meet format requirements.

Client Adaptation

LeanCloud Login:

// LeanCloud
const user = await AV.User.logIn(username, password);

CloudBase Login:

// CloudBase
import cloudbase from '@cloudbase/js-sdk';

const app = cloudbase.init({ env: 'your-env-id' });

// Username password login
const auth = app.auth();
await auth.signInWithUsernameAndPassword(username, password);

SDK Comparison Table

Below is an API comparison table between LeanCloud SDK and CloudBase SDK to help you quickly complete code migration.

Initialization

OperationLeanCloudCloudBase
Initialize SDKAV.init({ appId, appKey })cloudbase.init({ env })

LeanCloud:

const AV = require('leancloud-storage');
AV.init({
appId: 'your-app-id',
appKey: 'your-app-key',
});

CloudBase:

import cloudbase from '@cloudbase/js-sdk';
const app = cloudbase.init({ env: 'your-env-id' });

📖 CloudBase SDK Initialization Documentation


Database Operations

OperationLeanCloudCloudBase
Get database referencenew AV.Query('ClassName')app.database().collection('collectionName')
Query allquery.find()collection.get()
Query singlequery.get(objectId)collection.doc(id).get()
Conditional queryquery.equalTo('field', value)collection.where({ field: value })
Greater thanquery.greaterThan('field', value)collection.where({ field: _.gt(value) })
Less thanquery.lessThan('field', value)collection.where({ field: _.lt(value) })
Containsquery.containedIn('field', [values])collection.where({ field: _.in([values]) })
Sortquery.ascending('field')collection.orderBy('field', 'asc')
Limit countquery.limit(10)collection.limit(10)
Skip recordsquery.skip(10)collection.skip(10)
Add dataobject.save()collection.add(data)
Update dataobject.set('field', value).save()collection.doc(id).update({ field: value })
Delete dataobject.destroy()collection.doc(id).remove()
Batch deleteLoop destroy()collection.where(condition).remove()
Countquery.count()collection.count()

Query example comparison:

// LeanCloud
const query = new AV.Query('Todo');
query.equalTo('status', 'pending');
query.greaterThan('priority', 5);
query.ascending('createdAt');
query.limit(20);
const results = await query.find();

// CloudBase
const db = app.database();
const _ = db.command;
const results = await db.collection('Todo')
.where({
status: 'pending',
priority: _.gt(5)
})
.orderBy('createdAt', 'asc')
.limit(20)
.get();

📖 CloudBase Database API Documentation


Cloud Storage Operations

OperationLeanCloudCloudBase
Upload filenew AV.File(name, data).save()app.uploadFile({ cloudPath, filePath })
Get file URLfile.get('url')app.getTempFileURL({ fileList })
Delete filefile.destroy()app.deleteFile({ fileList })
Download fileDownload via URLapp.downloadFile({ fileID })

Upload file example comparison:

// LeanCloud
const file = new AV.File('avatar.png', fileInput.files[0]);
const savedFile = await file.save();
const url = savedFile.get('url');

// CloudBase
const result = await app.uploadFile({
cloudPath: 'avatars/avatar.png',
filePath: fileInput.files[0],
onUploadProgress: (progressEvent) => {
const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
console.log(`Upload progress: ${percent}%`);
}
});
const fileID = result.fileID;

Get file URL example comparison:

// LeanCloud
const url = file.get('url');

// CloudBase
const result = await app.getTempFileURL({
fileList: ['cloud://env-id.xxx/avatars/avatar.png']
});
const url = result.fileList[0].tempFileURL;

📖 CloudBase Cloud Storage API Documentation


Cloud Function Operations

OperationLeanCloudCloudBase
Call cloud functionAV.Cloud.run('functionName', params)app.callFunction({ name, data })
Define cloud functionAV.Cloud.define('name', handler)exports.main = async (event, context) => {}
Get caller inforequest.currentUsercontext.auth / event.userInfo

Call cloud function example comparison:

// LeanCloud client
const result = await AV.Cloud.run('hello', { name: 'World' });

// CloudBase client
const result = await app.callFunction({
name: 'hello',
data: { name: 'World' }
});
console.log(result.result);

Define cloud function example comparison:

// LeanCloud Cloud Engine
AV.Cloud.define('hello', async (request) => {
const { name } = request.params;
const user = request.currentUser;
return { message: `Hello, ${name}!` };
});

// CloudBase Cloud Function
exports.main = async (event, context) => {
const { name } = event;
const { openId, appId } = context.auth || {};
return { message: `Hello, ${name}!` };
};

📖 CloudBase Cloud Function API Documentation


Identity Authentication Operations

OperationLeanCloudCloudBase
Get Auth object-app.auth()
Username password registrationAV.User.signUp(username, password)auth.signUp({ username, password })
Username password loginAV.User.logIn(username, password)auth.signInWithUsernameAndPassword(username, password)
Phone verification code loginAV.User.signUpOrlogInWithMobilePhone(phone, code)auth.signInWithPhoneCode(phone, code)
Email password loginAV.User.logIn(email, password)auth.signInWithEmailAndPassword(email, password)
Anonymous loginAV.User.loginAnonymously()auth.signInAnonymously()
Get current userAV.User.current()auth.currentUser
Get login stateAV.User.current() !== nullauth.hasLoginState()
LogoutAV.User.logOut()auth.signOut()
Send verification codeAV.Cloud.requestSmsCode(phone)auth.getVerification({ phone_number })
Reset passwordAV.User.requestPasswordReset(email)auth.resetPassword({ email })
Update user infouser.set('field', value).save()auth.currentUser.update({ field: value })

Login example comparison:

// LeanCloud - Username password login
const user = await AV.User.logIn('username', 'password');
console.log('Login successful:', user.get('username'));

// CloudBase - Username password login
const auth = app.auth();
const loginState = await auth.signInWithUsernameAndPassword('username', 'password');
console.log('Login successful:', loginState.user);

Registration example comparison:

// LeanCloud - Username password registration
const user = await AV.User.signUp('username', 'password');

// CloudBase - Phone registration
const auth = app.auth();
// 1. Send verification code
const verification = await auth.getVerification({ phone_number: '+86 13800000000' });
// 2. Verify and register
const verifyResult = await auth.verify({
phone_number: '+86 13800000000',
verification_code: '123456',
verification_id: verification.verification_id
});
// 3. Complete registration
const loginState = await auth.signUp({
phone_number: '+86 13800000000',
verification_token: verifyResult.verification_token,
password: 'yourpassword'
});

📖 CloudBase Identity Authentication API Documentation


Real-time Database

OperationLeanCloudCloudBase
Real-time listeningliveQuery.subscribe()collection.watch()
Cancel listeningliveQuery.unsubscribe()watcher.close()

Real-time listening example comparison:

// LeanCloud LiveQuery
const query = new AV.Query('Message');
query.equalTo('roomId', 'room1');
const liveQuery = await query.subscribe();
liveQuery.on('create', (message) => {
console.log('New message:', message);
});

// CloudBase Real-time Data Push
const db = app.database();
const watcher = db.collection('Message')
.where({ roomId: 'room1' })
.watch({
onChange: (snapshot) => {
console.log('Data change:', snapshot.docChanges);
},
onError: (error) => {
console.error('Listening error:', error);
}
});

// Cancel listening
watcher.close();

📖 CloudBase Real-time Data Push Documentation


More API References

CategoryDocumentation Link
Web SDK Complete Documentationhttps://docs.cloudbase.net/api-reference/webv2/initialization
Node.js SDK Documentationhttps://docs.cloudbase.net/api-reference/server/initialization
Database Security Ruleshttps://docs.cloudbase.net/database/security-rules
Cloud Function Development Guidehttps://docs.cloudbase.net/cloud-function/introduce
Identity Authentication Guidehttps://docs.cloudbase.net/authentication/auth/introduce
Cloud Storage User Guidehttps://docs.cloudbase.net/storage/introduce

FAQ

Q: How to migrate LeanCloud ACL?

CloudBase uses security rules to replace ACL, need to configure database security rules in console. Refer to Security Rules Documentation.

Q: How to replace real-time communication features?

CloudBase supports real-time data push, can use database real-time listening feature:

const db = app.database();
db.collection('messages')
.where({ roomId: 'xxx' })
.watch({
onChange: (snapshot) => {
console.log('Data change:', snapshot.docs);
},
});

Q: How to ensure business continuity during migration?

Recommend gradual migration approach:

  1. First deploy CloudBase environment, conduct functional testing
  2. Use dual-write strategy, new data writes to both platforms simultaneously
  3. After completing data migration, gradually switch traffic
  4. After confirming stability, decommission LeanCloud service

Migration Checklist

Before completing migration, please confirm the following items:

  • Choose migration solution (CloudRun/Cloud Functions)
  • Export and convert LeanCloud data
  • Import data to CloudBase database
  • Remove LeanCloud SDK dependencies
  • Add CloudBase SDK dependencies
  • Refactor database operation code
  • Configure startup command and port (CloudRun)
  • Configure environment variables
  • Deploy to CloudRun/Cloud Functions
  • Migrate scheduled tasks (if any)
  • Encapsulate Hook logic via cloud functions (if have beforeSave/afterSave)
  • Migrate file storage
  • Migrate user system
  • Configure custom domain
  • Configure security rules (replace ACL)
  • Test all API endpoints
  • Test scheduled tasks
  • Test Hook logic (cloud functions)
  • Verify user login process