Migrate from LeanCloud
This document helps you migrate your project from LeanCloud to CloudBase.
Migration Overview
Feature Comparison
| LeanCloud | CloudBase | Description |
|---|---|---|
| Data Storage | Document Database | Both are JSON document storage |
| Cloud Engine | Cloud Functions/CloudRun | Backend code hosting |
| File Service | Cloud Storage | File storage service |
| User System | Identity Authentication | User 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:
- Go to "Data Storage" → "Data Management"
- Select the Class to export
- Click "Export", select JSONL format (one JSON object per line)
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 Field | CloudBase Field | Description |
|---|---|---|
objectId | _id | Data unique ID |
objectId | _openid | CloudBase user ID |
objectId | leancloud_objectId | Retain original ID for data tracing |
createdAt | _createTime | ISO 8601 → millisecond timestamp |
updatedAt | _updateTime | ISO 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 |
authData | uid | Extract 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 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:
- Login to CloudBase Console
- Go to "Document Database" → "Data Management"
- Create corresponding collections
- Click "Import" to upload converted JSON files
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
- 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
Please refer to LeanCloud Cloud Engine Migration to CloudRun/Cloud Functions Guide for detailed migration steps and code examples.
Quick Reference
| LeanCloud Cloud Engine Feature | CloudBase Corresponding Solution |
|---|---|
leanengine.yaml configuration | CloudRun 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 variables | CloudRun/Cloud Function environment variables |
Environment Variable Migration
Configure environment variables in CloudRun or Cloud Function console to replace original LeanCloud environment variables:
| LeanCloud Environment Variable | CloudBase Alternative | Description |
|---|---|---|
LEANCLOUD_APP_ID | ENV_ID | CloudBase environment ID |
LEANCLOUD_APP_KEY | Not needed | CloudRun internal auto-authentication |
LEANCLOUD_APP_MASTER_KEY | Not needed | CloudRun internal auto-authentication |
LEANCLOUD_APP_PORT | PORT | Service listening port |
LEANCLOUD_APP_ENV | TCB_ENV | Environment 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' }
});
- 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.
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
_Filetable - ✅ 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
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
};
LeanCloud Configuration:
- Login to LeanCloud Console
- Select your application
- Go to "Settings" → "App Credentials", get
App IDandApp Key - Go to "Settings" → "Domain Binding", get API domain (
serverURL) and file domain (fileDomain)
Tencent CloudBase Configuration:
- Login to Tencent CloudBase Platform
- Select your environment, get environment ID (
env) - Login to Tencent Cloud API Key Management
- Create or view key, get
SecretIdandSecretKey
⚠️ 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
- Query file list: Query all files from LeanCloud
_Filetable - Download files: Construct URL based on
keyand download to local temporary directory - Upload files: Upload files to Tencent CloudBase Cloud Storage
- Generate report: Generate
migration_report.jsonreport file - 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
- Network Stability: Ensure stable network connection, large file migration may take longer
- Disk Space: Ensure sufficient disk space to store temporary files
- Concurrency Count: Adjust concurrency based on network bandwidth, avoid too high causing request failures
- Retry Mechanism: Failed files will automatically retry, final failures will be recorded in the report
- Cloud Path: All files will be uploaded to
migrated/directory, can be modified as needed - fileID Saving: Uploaded
fileIDwill be recorded in the report, recommend saving for future use
Troubleshooting
Download Failed
- Check if
fileDomainconfiguration is correct - Check if network connection is normal
- Try increasing
timeouttimeout
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
objectIdto CloudBase user table (using admin API'sUidfield), 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 Method | LeanCloud | CloudBase |
|---|---|---|
| Username + Password | ✅ Built-in support | ✅ Built-in support |
| Email + Verification Code | ✅ Built-in support | ✅ Built-in support |
| Phone + Verification Code | ✅ Built-in support | ✅ Built-in support |
| WeChat Login | ✅ Third-party login | ✅ Deep 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 supported | ✅ Supported |
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 byUid. - 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
Nameto 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.
- CloudBase admin API allows
Migration Process
- Export LeanCloud user data (export from
_Useras JSON) - Field mapping and cleaning (
objectId→Uid, handle correspondence ofname/phone/email) - Call CloudBase admin API to batch create users (
CreateUser) - Synchronize custom business fields (write to self-built collection, linked by
Uid) - Configure security rules and permission logic (replace LeanCloud ACL)
- 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 Field | Description | CloudBase Field | Write Method |
|---|---|---|---|
objectId | User unique identifier | Uid | Required: Write to CreateUser.Uid, achieving "original ID remains unchanged" |
name / username | Login username (may be username / phone / email address) | Name | Recommend priority writing to Name |
mobilePhoneNumber | Phone number | Phone | If exists and compliant, write to Phone, after writing user can use phone verification code login |
email | Email | If exists and compliant, write to Email, after writing user can use email verification code login | |
nickname | Nickname | NickName | Optional |
avatar / avatarUrl | Avatar link | AvatarUrl | Optional (need public network accessible) |
| Other business fields | E.g., membership level, channel, invitation relationship, etc. | Not recommend writing to user system table | Recommend writing to self-built collection user_profile, linked by Uid |
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
nameto CloudBase'sName, also need to synchronize LeanCloud'snameto CloudBase'sPhoneand/orEmailfield, otherwise users will not be able to login in the original way. - Practical suggestions:
- If
nameis like email (contains@and meets email format) → WriteEmail = name - If
nameis like phone (11 digits, etc.) → WritePhone = name - If
nameis not phone/email, don't force write toPhone/Email, please use your originalmobilePhoneNumber/emailfield, or guide users to change to verification code login.
- If
Password Migration Strategy
Because original password cannot be migrated, usually three feasible solutions:
| Solution | Description | Applicable Scenario |
|---|---|---|
| Solution 1: Verification Code Login Priority | After migration, guide users to login with SMS/email verification code, then reset password on "Set Password" page | Recommended, good user experience |
| Solution 2: Set Temporary Password | Generate temporary password for each user during import (script can generate random strong password), force change after first login | Need to notify users of temporary password |
| Solution 3: Login State Migration | If 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 system | Complex scenario |
Migration Script
We provide a one-click migration script with features including:
- ✅ Read LeanCloud exported user JSON (array)
- ✅ Call CloudBase admin API
CreateUserto batch create users - ✅ Write
objectIdtoUid, keep original ID unchanged - ✅ If user already exists (returns duplicate data), automatically call
ModifyUserto update fields (default does not reset password) - ✅ Optional: Mirror
nametoPhone/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 Variable | Description | Required |
|---|---|---|
TENCENT_SECRET_ID | Tencent Cloud SecretId, Get here | ✅ Required |
TENCENT_SECRET_KEY | Tencent Cloud SecretKey, Get here | ✅ Required |
TENCENT_ENV_ID | CloudBase environment ID | ✅ Required |
INPUT_FILE | Import file path (default ./leancloud_users.json) | Optional |
USER_TYPE | User type: internalUser (internal) / externalUser (external) | Optional |
TENCENT_REGION | Region (e.g., ap-shanghai) | Optional |
TENCENT_ENDPOINT | API endpoint (default tcb.tencentcloudapi.com) | Optional |
CONCURRENCY | Concurrency (default 3) | Optional |
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):
| Mode | Environment Variable | Description |
|---|---|---|
| Don't write password (default) | SET_PASSWORD_MODE=none | Users need to reset password after verification code login |
| Unified default password | SET_PASSWORD_MODE=default + DEFAULT_PASSWORD=xxx | Not recommended, only for test environment |
| Random temporary password | SET_PASSWORD_MODE=random | Recommended, generate random strong password for each user |
Duplicate import behavior:
- Default: If
CreateUserreturns duplicate data, script will useModifyUserto update fields, will not reset password UPDATE_PASSWORD_ON_DUPLICATE=true: Allow updating password according toSET_PASSWORD_MODEon duplicate import (generally not recommended)
Login name mirroring:
MIRROR_NAME_TO_LOGIN=true: Whennamelooks like phone/email, automatically sync toPhone/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
DescribeUserListon CloudBase side to query if correspondingUid/Name/Phone/Emailexists - 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 100Uidat 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/permissionsin 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
| Operation | LeanCloud | CloudBase |
|---|---|---|
| Initialize SDK | AV.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
| Operation | LeanCloud | CloudBase |
|---|---|---|
| Get database reference | new AV.Query('ClassName') | app.database().collection('collectionName') |
| Query all | query.find() | collection.get() |
| Query single | query.get(objectId) | collection.doc(id).get() |
| Conditional query | query.equalTo('field', value) | collection.where({ field: value }) |
| Greater than | query.greaterThan('field', value) | collection.where({ field: _.gt(value) }) |
| Less than | query.lessThan('field', value) | collection.where({ field: _.lt(value) }) |
| Contains | query.containedIn('field', [values]) | collection.where({ field: _.in([values]) }) |
| Sort | query.ascending('field') | collection.orderBy('field', 'asc') |
| Limit count | query.limit(10) | collection.limit(10) |
| Skip records | query.skip(10) | collection.skip(10) |
| Add data | object.save() | collection.add(data) |
| Update data | object.set('field', value).save() | collection.doc(id).update({ field: value }) |
| Delete data | object.destroy() | collection.doc(id).remove() |
| Batch delete | Loop destroy() | collection.where(condition).remove() |
| Count | query.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
| Operation | LeanCloud | CloudBase |
|---|---|---|
| Upload file | new AV.File(name, data).save() | app.uploadFile({ cloudPath, filePath }) |
| Get file URL | file.get('url') | app.getTempFileURL({ fileList }) |
| Delete file | file.destroy() | app.deleteFile({ fileList }) |
| Download file | Download via URL | app.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
| Operation | LeanCloud | CloudBase |
|---|---|---|
| Call cloud function | AV.Cloud.run('functionName', params) | app.callFunction({ name, data }) |
| Define cloud function | AV.Cloud.define('name', handler) | exports.main = async (event, context) => {} |
| Get caller info | request.currentUser | context.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
| Operation | LeanCloud | CloudBase |
|---|---|---|
| Get Auth object | - | app.auth() |
| Username password registration | AV.User.signUp(username, password) | auth.signUp({ username, password }) |
| Username password login | AV.User.logIn(username, password) | auth.signInWithUsernameAndPassword(username, password) |
| Phone verification code login | AV.User.signUpOrlogInWithMobilePhone(phone, code) | auth.signInWithPhoneCode(phone, code) |
| Email password login | AV.User.logIn(email, password) | auth.signInWithEmailAndPassword(email, password) |
| Anonymous login | AV.User.loginAnonymously() | auth.signInAnonymously() |
| Get current user | AV.User.current() | auth.currentUser |
| Get login state | AV.User.current() !== null | auth.hasLoginState() |
| Logout | AV.User.logOut() | auth.signOut() |
| Send verification code | AV.Cloud.requestSmsCode(phone) | auth.getVerification({ phone_number }) |
| Reset password | AV.User.requestPasswordReset(email) | auth.resetPassword({ email }) |
| Update user info | user.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
| Operation | LeanCloud | CloudBase |
|---|---|---|
| Real-time listening | liveQuery.subscribe() | collection.watch() |
| Cancel listening | liveQuery.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
| Category | Documentation Link |
|---|---|
| Web SDK Complete Documentation | https://docs.cloudbase.net/api-reference/webv2/initialization |
| Node.js SDK Documentation | https://docs.cloudbase.net/api-reference/server/initialization |
| Database Security Rules | https://docs.cloudbase.net/database/security-rules |
| Cloud Function Development Guide | https://docs.cloudbase.net/cloud-function/introduce |
| Identity Authentication Guide | https://docs.cloudbase.net/authentication/auth/introduce |
| Cloud Storage User Guide | https://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:
- First deploy CloudBase environment, conduct functional testing
- Use dual-write strategy, new data writes to both platforms simultaneously
- After completing data migration, gradually switch traffic
- 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