Skip to main content

Read and Write CloudBase Database in WeChat Mini Program

In one sentence: In a WeChat Mini Program already integrated with CloudBase Custom Login, use app.database() for collection CRUD, conditional queries, and real-time watch subscriptions — all reads and writes are constrained by the collection's Permission Mode.

Estimated time: 30 minutes | Difficulty: Advanced

Applicable Scenarios

This is the next step after add-auth-wechat-miniprogram. The prerequisite has already installed @cloudbase/js-sdk and obtained a login state; this article covers how to read and write data.

  • Applicable: Standalone CloudBase environment + WeChat Mini Program frontend, login state already established
  • Not applicable: Using the wx.cloud WeChat Cloud Development system (that system has its own wx.cloud.database(), with a similar but incompatible API)
  • Not applicable: Financial/inventory scenarios requiring extremely high consistency. Frontend direct database access uses "permission-constrained SDK calls"; under extreme concurrency, it is recommended to route writes through Cloud Functions

Prerequisites

DependencyVersion
@cloudbase/js-sdk2.27.3 (already installed in the prerequisite recipe)
@cloudbase/adapter-wx_mp1.3.1
WeChat DevTools1.06.x

Also required:

  • Already completed add-auth-wechat-miniprogram in the Mini Program, with auth.hasLoginState() returning true
  • CloudBase Console access to "Database → Collection Management" for the target collection

Step 1: Create a Collection and Select a Permission Mode

Go to CloudBase Console → Database → Collection Management → Create Collection. For example, create a collection named todos.

After creation, click into it; the "Data Permission" button appears in the upper left — this is the key part of this step. CloudBase provides four built-in Permission Modes:

Permission ModeWho Can ReadWho Can Write
All users can read, only creator can writeAll logged-in usersOnly records where _openid equals the current user's openid
Only creator can read and writeOnly records where _openid equals the current user's openidSame as left
Only admin can read and writeCloud Functions and ConsoleSame as left
Custom Security RulesSee secure-database-multi-tenant-rulesSame as left

"Only creator can read and write" is the default choice for todo / personal notes scenarios. CloudBase automatically adds an _openid field to documents on write; subsequent reads and writes are filtered by this field, so no need to write where({ _openid: ... }) in business code.

If "Only admin can read and write" is selected, all reads and writes from the Mini Program frontend will return UNAUTHORIZED; those must be routed through Cloud Functions, which is not covered here.

Step 2: Get a db Instance in the Mini Program

Continuing from the miniprogram/libs/cloudbase.js code in add-auth-wechat-miniprogram, add one line:

import cloudbase from '@cloudbase/js-sdk';
import adapter from '@cloudbase/adapter-wx_mp';

cloudbase.useAdapters(adapter);

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

export const auth = app.auth();
export const db = app.database();
export default app;

The db returned by app.database() is a lightweight object that can be imported repeatedly without concern about creating multiple instances.

To prevent a page from issuing queries before the login state is ready, it is recommended to await ensureLogin() before using db:

import { ensureLogin } from './login';
import { db } from './cloudbase';

export async function getTodos() {
await ensureLogin();
return db.collection('todos').where({}).orderBy('createdAt', 'desc').get();
}

Step 3: CRUD

Create

const res = await db.collection('todos').add({
title: 'Write recipe',
done: false,
createdAt: db.serverDate(),
});
console.log('inserted id:', res.id);

db.serverDate() is a server-side timestamp placeholder replaced with the current database time on write, preventing client clock drift from causing sort issues.

Read

// Get all
const all = await db.collection('todos').get();

// Conditional + sort + pagination
const page = await db
.collection('todos')
.where({ done: false })
.orderBy('createdAt', 'desc')
.skip(0)
.limit(20)
.get();

console.log('docs:', page.data);

The return structure is { data: [...], requestId: '...' }. The document list is in data. A common mistake is writing page.docs (that is the field in watch callbacks, see Step 4).

For complex conditions, use db.command:

const _ = db.command;
const recent = await db
.collection('todos')
.where({
createdAt: _.gte(new Date(Date.now() - 7 * 24 * 3600 * 1000)),
done: _.eq(false),
})
.get();

Update

Update a single document by _id:

await db.collection('todos').doc('todo-id').update({
done: true,
updatedAt: db.serverDate(),
});

update performs a partial update, overwriting only the provided fields. To replace the entire document (including deleting fields that are not passed), use set:

await db.collection('todos').doc('todo-id').set({
title: 'Full replacement',
done: true,
// Fields not passed (e.g., the original createdAt) will be removed
});

Bulk update by condition:

await db.collection('todos').where({ done: true }).update({
archived: true,
});

Delete

// By ID
await db.collection('todos').doc('todo-id').remove();

// By condition
await db.collection('todos').where({ archived: true }).remove();

Step 4: Real-time watch Subscription

watch lets the frontend subscribe to the result set of a query; whenever records change, updates are pushed via onChange.

// pages/todos/todos.js
import { db } from '../../libs/cloudbase';

Page({
data: { todos: [] },

onLoad() {
this.watcher = db
.collection('todos')
.where({ done: false })
.watch({
onChange: (snapshot) => {
// snapshot.docs is the full current result set for this query
this.setData({ todos: snapshot.docs });
},
onError: (err) => {
console.error('[watch] error', err);
},
});
},

onUnload() {
// Must close when the page is destroyed, otherwise memory leaks and duplicate pushes occur
if (this.watcher) {
this.watcher.close();
this.watcher = null;
}
},
});

Four key points:

  • watch() returns a closer; subscription can only be cancelled via closer.close(), not removeListener
  • The first callback delivers an "initial snapshot" containing all documents matching the query, similar to a get()
  • On every subsequent add / update / delete of a matching document, onChange fires; snapshot.docs is the complete result set after the change, not a diff
  • onUnload on pages / detached on components must call close(); leaving it open too long triggers the server-side connection limit

To get the diff (only the changed documents), use snapshot.docChanges:

onChange: (snapshot) => {
for (const change of snapshot.docChanges) {
// change.dataType: 'init' | 'update' | 'add' | 'remove'
console.log(change.dataType, change.doc);
}
};

Verification

  1. Compile and run in WeChat DevTools
  2. On the todos page, call add to insert a record; the Console should print the inserted id
  3. Console → Database → todos collection: confirm the data has an _openid field equal to the current user's openid
  4. Directly edit a record's title in the Console; the Mini Program page should immediately reflect the change (watch is working)
  5. Exit the page; the Console should no longer log watch push messages

Common Errors

Error code / messageCauseFix
UNAUTHORIZED / permission deniedCollection Permission Mode is "Only admin can read and write", or the document's _openid does not match the current userCheck the collection Permission Mode, or confirm the login state is valid at write time
collection not existThe collection has not been created in the ConsoleCreate it manually in the Console; the SDK does not auto-create collections
INVALID_ARGUMENT with field type infoThe same field has inconsistent types across documents (e.g., some string, some number)CloudBase does not enforce schema, but mixed types cause errors during comparison/sorting. Unify types, or call Number(x) before writing
watch onChange not firingThe page closer was likely overwritten multiple times in onLoad without closing the old oneGuard with if (this.watcher) this.watcher.close() to keep only one active subscription
WX_ERR_NETWORKThe Mini Program's allowed domains are not configuredWeChat Official Account Platform → Development Management → Server Domain → add *.tcloudbase.com to request allowed domains

For error code definitions, see error-code.

Next Steps