mobile-storage-sqlite-powersync
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSQLite + PowerSync Patterns
SQLite + PowerSync 模式
Quick Guide: Usefor offline-first apps backed by local SQLite. Define schemas with@powersync/react-nativeandTable(id column is auto-created). Usecolumn.text/integer/realfor reads/writes,PowerSyncDatabasefromuseQueryfor reactive watched queries. Connect to your backend via a connector implementing@powersync/react+fetchCredentials. Conflict resolution defaults to last-write-wins per field -- customize inuploadData. UseuploadDatafor SQLCipher encryption.@powersync/op-sqlite
<critical_requirements>
快速指南: 使用构建基于本地SQLite的优先离线应用。通过@powersync/react-native和Table定义schema(id列会自动创建)。使用column.text/integer/real进行读写操作,使用PowerSyncDatabase中的@powersync/react实现响应式监听查询。通过实现useQuery+fetchCredentials的连接器对接后端。冲突解决默认采用按字段最后写入获胜规则——可在uploadData中自定义。使用uploadData实现SQLCipher加密。@powersync/op-sqlite
<critical_requirements>
CRITICAL: Before Using This Skill
重要提示:使用本技能前
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,, named constants)import type
(You MUST define schemas with using , , -- NEVER declare an column, PowerSync creates it automatically)
new Table({ ... })column.textcolumn.integercolumn.realid(You MUST call after to start syncing -- without it the database is local-only with no sync)
powersync.connect(connector)init()(You MUST implement both and in your backend connector -- missing either breaks the sync loop)
fetchCredentials()uploadData()(You MUST use from for reactive queries -- raw does NOT re-render on data changes)
useQuery@powersync/reactgetAll()</critical_requirements>
Auto-detection: PowerSync, powersync, @powersync/react-native, @powersync/react, @powersync/op-sqlite, PowerSyncDatabase, useQuery, usePowerSync, useStatus, useSuspenseQuery, PowerSyncBackendConnector, fetchCredentials, uploadData, column.text, column.integer, column.real, Schema, Table, sync rules, bucket_definitions, offline-first SQLite, watched query, CrudEntry, CrudTransaction, AttachmentQueue, AttachmentTable, local-only table
When to use:
- Building offline-first React Native apps that sync with a cloud database
- Storing relational data locally in SQLite with automatic cloud sync
- Implementing reactive UIs that update when synced data changes
- Handling CRUD operations that work offline and sync when reconnected
- Defining sync rules (bucket definitions) for partial data replication
- Managing file attachments with offline upload/download queues
Key patterns covered:
- Schema definition with ,
Tabletypes, indexes, and local-only tablescolumn - setup with default or OP-SQLite adapter
PowerSyncDatabase - React hooks: ,
useQuery,useSuspenseQuery,useStatususePowerSync - Backend connector: +
fetchCredentials()implementationuploadData() - CRUD operations via ,
execute(),get(),getAll()getOptional() - Sync rules with bucket definitions (YAML) for per-user data filtering
- Conflict resolution strategies (last-write-wins, field-level, custom)
- Attachment handling with and
AttachmentTableAttachmentQueue - OP-SQLite integration for SQLCipher encryption
When NOT to use:
- Simple key-value storage without sync (use a key-value store)
- Apps that never go offline and always have connectivity
- Data that does not need relational queries (use a key-value store)
- File-only storage without structured metadata (use the filesystem)
Detailed Resources:
- examples/core.md - Schema, database setup, CRUD, watched queries, hooks
- examples/sync.md - Backend connector, sync rules, conflict resolution
- examples/attachments.md - Attachment queue, upload/download, storage adapters
- reference.md - API reference, setup checklist
<philosophy>
所有代码必须遵循CLAUDE.md中的项目约定(短横线命名法、命名导出、导入顺序、、命名常量)import type
(必须使用结合、、定义schema——绝不能声明列,PowerSync会自动创建)
new Table({ ... })column.textcolumn.integercolumn.realid(必须在后调用以启动同步——否则数据库仅为本地模式,无法同步)
init()powersync.connect(connector)(必须在后端连接器中同时实现和——缺少任意一个都会破坏同步循环)
fetchCredentials()uploadData()(必须使用中的实现响应式查询——原生不会在数据变化时重新渲染)
@powersync/reactuseQuerygetAll()</critical_requirements>
自动检测关键词: PowerSync, powersync, @powersync/react-native, @powersync/react, @powersync/op-sqlite, PowerSyncDatabase, useQuery, usePowerSync, useStatus, useSuspenseQuery, PowerSyncBackendConnector, fetchCredentials, uploadData, column.text, column.integer, column.real, Schema, Table, sync rules, bucket_definitions, offline-first SQLite, watched query, CrudEntry, CrudTransaction, AttachmentQueue, AttachmentTable, local-only table
适用场景:
- 构建可与云数据库同步的优先离线React Native应用
- 在本地SQLite中存储关系型数据并实现自动云同步
- 实现可随同步数据变化而更新的响应式UI
- 处理可离线运行、重新连接后自动同步的CRUD操作
- 定义同步规则(bucket定义)以实现部分数据复制
- 通过离线上传/下载队列管理文件附件
涵盖的核心模式:
- 使用、
Table类型、索引和本地专属表定义schemacolumn - 使用默认或OP-SQLite适配器设置
PowerSyncDatabase - React钩子:、
useQuery、useSuspenseQuery、useStatususePowerSync - 后端连接器:+
fetchCredentials()实现uploadData() - 通过、
execute()、get()、getAll()执行CRUD操作getOptional() - 使用bucket定义(YAML)实现按用户过滤数据的同步规则
- 冲突解决策略(最后写入获胜、字段级、自定义)
- 使用和
AttachmentTable处理附件AttachmentQueue - 集成OP-SQLite实现SQLCipher加密
不适用场景:
- 无需同步的简单键值存储(使用键值存储工具)
- 始终在线、从不离线的应用
- 无需关系型查询的数据(使用键值存储工具)
- 仅存储文件、无结构化元数据的场景(使用文件系统)
详细资源:
- examples/core.md - Schema、数据库设置、CRUD、监听查询、钩子
- examples/sync.md - 后端连接器、同步规则、冲突解决
- examples/attachments.md - 附件队列、上传/下载、存储适配器
- reference.md - API参考、设置清单
<philosophy>
Philosophy
设计理念
PowerSync is an offline-first sync engine that sits on top of SQLite. The core idea: your app reads and writes to a local SQLite database instantly (no network calls), and PowerSync handles bidirectional sync with your cloud database in the background.
Core principles:
- Local-first -- all reads and writes hit local SQLite, so the app works instantly and offline
- Sync is transparent -- PowerSync streams changes from the server and uploads local mutations automatically
- Schema drives everything -- the client schema defines local tables, the server sync rules define what data each client receives
- Conflict resolution is yours -- defaults to last-write-wins, but gives you full control
uploadData() - Watched queries for reactivity -- re-executes queries when dependent tables change, keeping UI in sync
useQuery
Architecture overview:
Client (React Native) Cloud
+-------------------+ +-------------------+
| Local SQLite DB | <-sync->| PowerSync Service |<--- Source DB (Postgres, etc.)
| (PowerSyncDatabase)| | (Sync Rules) |
+-------------------+ +-------------------+
| @powersync/react | | Bucket Defs |
| (useQuery, etc.) | | (YAML config) |
+-------------------+ +-------------------+Data flow:
- Writes: App calls on local SQLite. PowerSync queues the change and calls your
execute(INSERT/UPDATE/DELETE)to push it to the backend.uploadData() - Reads: Sync rules on the server determine which data each client receives. The PowerSync Service streams changes to the client's local SQLite. watches for table changes and re-renders.
useQuery
Column types: Only three types exist -- , , . The column (text, primary key) is auto-created. If a synced value doesn't match the declared type, it is cast automatically.
</philosophy>
column.textcolumn.integercolumn.realid<patterns>
PowerSync 是一款基于 SQLite 的优先离线同步引擎。核心思想:应用直接读写本地SQLite数据库(无需网络调用),PowerSync在后台处理与云数据库的双向同步。
核心原则:
- 本地优先——所有读写操作均针对本地SQLite,应用可立即响应并离线运行
- 同步透明——PowerSync自动从服务器流式获取变更,并上传本地修改
- Schema驱动——客户端schema定义本地表,服务器同步规则定义每个客户端接收的数据
- 冲突自主解决——默认采用最后写入获胜规则,但赋予你完全控制权
uploadData() - 监听查询实现响应式——会在关联表变化时重新执行查询,保持UI同步
useQuery
架构概览:
客户端(React Native) 云端
+-------------------+ +-------------------+
| 本地SQLite数据库 | <-同步->| PowerSync服务 |<--- 源数据库(Postgres等)
| (PowerSyncDatabase)| | (同步规则) |
+-------------------+ +-------------------+
| @powersync/react | | Bucket定义 |
| (useQuery等) | | (YAML配置) |
+-------------------+ +-------------------+数据流:
- 写入: 应用调用本地SQLite的。PowerSync将变更加入队列,并调用你的
execute(INSERT/UPDATE/DELETE)推送到后端。uploadData() - 读取: 服务器端的同步规则决定每个客户端接收的数据。PowerSync服务将变更流式传输到客户端的本地SQLite。监听表变化并重新渲染。
useQuery
列类型: 仅支持三种类型——、、。列(文本类型,主键)会自动创建。如果同步值与声明类型不匹配,会自动转换。
</philosophy>
column.textcolumn.integercolumn.realid<patterns>
Core Patterns
核心模式
Pattern 1: Schema Definition
模式1:Schema定义
Define your client-side schema using and types. The schema mirrors your server tables (minus the column, which is auto-created).
Tablecolumnidtypescript
import { column, Schema, Table } from "@powersync/react-native";
const lists = new Table({
created_at: column.text,
name: column.text,
owner_id: column.text,
});
const todos = new Table(
{
list_id: column.text,
created_at: column.text,
completed_at: column.text,
description: column.text,
completed: column.integer,
},
{ indexes: { list: ["list_id"] } },
);
export const AppSchema = new Schema({ todos, lists });
// Derive types from schema
export type Database = (typeof AppSchema)["types"];
export type TodoRecord = Database["todos"];
export type ListRecord = Database["lists"];Why good: Schema is source of truth for types, indexes optimize query performance, no manual column needed
idGotcha: Table names in the schema must match table names in your sync rules. Mismatches cause data to silently not sync.
See examples/core.md for local-only tables and index configuration.
使用和类型定义客户端schema。schema需与服务器表对应(无需包含列,会自动创建)。
Tablecolumnidtypescript
import { column, Schema, Table } from "@powersync/react-native";
const lists = new Table({
created_at: column.text,
name: column.text,
owner_id: column.text,
});
const todos = new Table(
{
list_id: column.text,
created_at: column.text,
completed_at: column.text,
description: column.text,
completed: column.integer,
},
{ indexes: { list: ["list_id"] } },
);
export const AppSchema = new Schema({ todos, lists });
// 从schema推导类型
export type Database = (typeof AppSchema)["types"];
export type TodoRecord = Database["todos"];
export type ListRecord = Database["lists"];优势: Schema是类型的唯一来源,索引优化查询性能,无需手动定义列
id注意事项: Schema中的表名必须与同步规则中的表名一致。不匹配会导致数据无法同步且无提示。
查看examples/core.md了解本地专属表和索引配置。
Pattern 2: PowerSyncDatabase Setup
模式2:PowerSyncDatabase设置
Create the database instance at app startup. Choose between the default SQLite adapter or OP-SQLite for encryption.
typescript
import { PowerSyncDatabase } from "@powersync/react-native";
import { AppSchema } from "./schema";
const DB_FILENAME = "app.db";
export const powersync = new PowerSyncDatabase({
schema: AppSchema,
database: { dbFilename: DB_FILENAME },
});
// Initialize and connect (typically in app bootstrap)
async function initDatabase(connector: PowerSyncBackendConnector) {
await powersync.init();
await powersync.connect(connector);
}Why good: Single instance shared across app, creates SQLite tables from schema, starts bidirectional sync
init()connect()Gotcha: Without , the database works but is purely local -- no sync occurs.
connect()See examples/core.md for OP-SQLite setup with encryption and the React context provider pattern.
在应用启动时创建数据库实例。可选择默认SQLite适配器或OP-SQLite实现加密。
typescript
import { PowerSyncDatabase } from "@powersync/react-native";
import { AppSchema } from "./schema";
const DB_FILENAME = "app.db";
export const powersync = new PowerSyncDatabase({
schema: AppSchema,
database: { dbFilename: DB_FILENAME },
});
// 初始化并连接(通常在应用启动时执行)
async function initDatabase(connector: PowerSyncBackendConnector) {
await powersync.init();
await powersync.connect(connector);
}优势: 应用内共享单一实例,根据schema创建SQLite表,启动双向同步
init()connect()注意事项: 若未调用,数据库仅能本地使用——无法同步。
connect()查看examples/core.md了解带加密的OP-SQLite设置和React上下文提供者模式。
Pattern 3: React Hooks for Reactive Queries
模式3:React钩子实现响应式查询
Use from for watched queries that re-execute when dependent tables change. Wrap your app in .
useQuery@powersync/reactPowerSyncContext.Providertsx
import { useQuery, useStatus, usePowerSync } from "@powersync/react";
function TodoList({ listId }: { listId: string }) {
const {
data: todos,
isLoading,
error,
} = useQuery<TodoRecord>(
"SELECT * FROM todos WHERE list_id = ? ORDER BY created_at DESC",
[listId],
);
if (isLoading) return <ActivityIndicator />;
if (error) return <Text>Error: {error.message}</Text>;
return (
<FlatList
data={todos}
renderItem={({ item }) => <TodoItem todo={item} />}
keyExtractor={(item) => item.id}
/>
);
}Why good: automatically re-runs when the table changes (insert, update, delete), and handle loading/error states
useQuerytodosisLoadingerrorSee examples/core.md for , , , and usage.
useSuspenseQueryuseStatususePowerSyncrunQueryOnce使用中的实现监听查询,当关联表变化时自动重新执行。将应用包裹在中。
@powersync/reactuseQueryPowerSyncContext.Providertsx
import { useQuery, useStatus, usePowerSync } from "@powersync/react";
function TodoList({ listId }: { listId: string }) {
const {
data: todos,
isLoading,
error,
} = useQuery<TodoRecord>(
"SELECT * FROM todos WHERE list_id = ? ORDER BY created_at DESC",
[listId],
);
if (isLoading) return <ActivityIndicator />;
if (error) return <Text>Error: {error.message}</Text>;
return (
<FlatList
data={todos}
renderItem={({ item }) => <TodoItem todo={item} />}
keyExtractor={(item) => item.id}
/>
);
}优势: 会在表发生变更(插入、更新、删除)时自动重新执行,和处理加载/错误状态
useQuerytodosisLoadingerror查看examples/core.md了解、、和的用法。
useSuspenseQueryuseStatususePowerSyncrunQueryOncePattern 4: CRUD Operations
模式4:CRUD操作
All writes use with parameterized SQL. PowerSync queues changes and calls your to sync.
execute()uploadData()typescript
import { usePowerSync } from "@powersync/react";
function useTodos(listId: string) {
const powersync = usePowerSync();
const addTodo = async (description: string) => {
await powersync.execute(
"INSERT INTO todos (id, list_id, description, created_at, completed) VALUES (uuid(), ?, ?, datetime(), 0)",
[listId, description],
);
};
const toggleTodo = async (id: string, completed: boolean) => {
const completedAt = completed ? new Date().toISOString() : null;
await powersync.execute(
"UPDATE todos SET completed = ?, completed_at = ? WHERE id = ?",
[completed ? 1 : 0, completedAt, id],
);
};
const deleteTodo = async (id: string) => {
await powersync.execute("DELETE FROM todos WHERE id = ?", [id]);
};
return { addTodo, toggleTodo, deleteTodo };
}Why good: Writes hit local SQLite instantly (no network wait), generates IDs client-side, parameterized queries prevent SQL injection
uuid()Gotcha: returns . When using views, may return 0 -- use clauses to confirm mutations.
execute(){ rowsAffected, insertId }rowsAffectedRETURNINGSee examples/core.md for , , , and transaction patterns.
get()getAll()getOptional()所有写入操作使用带参数化SQL的。PowerSync会将变更加入队列,并调用进行同步。
execute()uploadData()typescript
import { usePowerSync } from "@powersync/react";
function useTodos(listId: string) {
const powersync = usePowerSync();
const addTodo = async (description: string) => {
await powersync.execute(
"INSERT INTO todos (id, list_id, description, created_at, completed) VALUES (uuid(), ?, ?, datetime(), 0)",
[listId, description],
);
};
const toggleTodo = async (id: string, completed: boolean) => {
const completedAt = completed ? new Date().toISOString() : null;
await powersync.execute(
"UPDATE todos SET completed = ?, completed_at = ? WHERE id = ?",
[completed ? 1 : 0, completedAt, id],
);
};
const deleteTodo = async (id: string) => {
await powersync.execute("DELETE FROM todos WHERE id = ?", [id]);
};
return { addTodo, toggleTodo, deleteTodo };
}优势: 写入操作直接作用于本地SQLite(无需等待网络),在客户端生成ID,参数化查询防止SQL注入
uuid()注意事项: 返回。使用视图时,可能返回0——使用子句确认修改。
execute(){ rowsAffected, insertId }rowsAffectedRETURNING查看examples/core.md了解、、和事务模式。
get()getAll()getOptional()Pattern 5: Backend Connector
模式5:后端连接器
The connector bridges PowerSync with your backend. Implement for auth and for pushing local changes.
fetchCredentials()uploadData()typescript
import type {
PowerSyncBackendConnector,
PowerSyncCredentials,
} from "@powersync/react-native";
import type { AbstractPowerSyncDatabase } from "@powersync/react-native";
export const connector: PowerSyncBackendConnector = {
fetchCredentials: async (): Promise<PowerSyncCredentials> => {
// Return your PowerSync instance URL and a valid JWT
const session = await getAuthSession();
return {
endpoint: POWERSYNC_URL,
token: session.accessToken,
expiresAt: session.expiresAt,
};
},
uploadData: async (database: AbstractPowerSyncDatabase): Promise<void> => {
const transaction = await database.getNextCrudTransaction();
if (!transaction) return;
for (const op of transaction.crud) {
// Send each operation to your backend API
await applyOperation(op);
}
await transaction.complete();
},
};Why good: Clean separation of auth and data upload, transaction-based processing ensures atomicity, marks the batch as synced
complete()See examples/sync.md for the full Supabase connector, custom backend patterns, and error handling with retries.
连接器用于衔接PowerSync与你的后端。实现进行认证,推送本地变更。
fetchCredentials()uploadData()typescript
import type {
PowerSyncBackendConnector,
PowerSyncCredentials,
} from "@powersync/react-native";
import type { AbstractPowerSyncDatabase } from "@powersync/react-native";
export const connector: PowerSyncBackendConnector = {
fetchCredentials: async (): Promise<PowerSyncCredentials> => {
// 返回PowerSync实例URL和有效的JWT
const session = await getAuthSession();
return {
endpoint: POWERSYNC_URL,
token: session.accessToken,
expiresAt: session.expiresAt,
};
},
uploadData: async (database: AbstractPowerSyncDatabase): Promise<void> => {
const transaction = await database.getNextCrudTransaction();
if (!transaction) return;
for (const op of transaction.crud) {
// 将每个操作发送到后端API
await applyOperation(op);
}
await transaction.complete();
},
};优势: 清晰分离认证和数据上传,基于事务的处理确保原子性,标记批次已同步
complete()查看examples/sync.md了解完整的Supabase连接器、自定义后端模式和带重试的错误处理。
Pattern 6: Sync Rules (Bucket Definitions)
模式6:同步规则(Bucket定义)
Sync rules (YAML) define which server data each client receives. Configured on the PowerSync Service, not in client code.
yaml
bucket_definitions:
user_lists:
parameters: SELECT request.user_id() as user_id
data:
- SELECT * FROM lists WHERE owner_id = bucket.user_id
- SELECT * FROM todos WHERE list_id IN (
SELECT id FROM lists WHERE owner_id = bucket.user_id
)
global_settings:
# No parameters = global bucket, synced to all clients
data:
- SELECT * FROM settingsWhy good: Per-user data filtering at the server, global buckets for shared data, SQL-based rules are familiar
Gotcha: Maximum 1,000 buckets per client (default). Table names must match client schema.
See examples/sync.md for parameterized buckets, client parameters, and multi-tenant patterns.
同步规则(YAML)定义每个客户端从服务器接收的数据。在PowerSync服务端配置,而非客户端代码中。
yaml
bucket_definitions:
user_lists:
parameters: SELECT request.user_id() as user_id
data:
- SELECT * FROM lists WHERE owner_id = bucket.user_id
- SELECT * FROM todos WHERE list_id IN (
SELECT id FROM lists WHERE owner_id = bucket.user_id
)
global_settings:
# 无参数 = 全局bucket,同步到所有客户端
data:
- SELECT * FROM settings优势: 在服务器端实现按用户过滤数据,全局bucket用于共享数据,基于SQL的规则易于理解
注意事项: 默认每个客户端最多支持1000个bucket。表名必须与客户端schema一致。
查看examples/sync.md了解参数化bucket、客户端参数和多租户模式。
Pattern 7: Conflict Resolution
模式7:冲突解决
Default behavior is last-write-wins per field. Customize in your implementation.
uploadData()The key insight: PowerSync gives you full control in . You choose how to handle each operation -- accept, reject, merge, or record conflicts.
uploadData()CrudEntryCommon strategies:
- Last-write-wins (default): Simply upsert each operation
- Timestamp-based: Compare client vs server timestamps, reject stale writes
- Field-level merge: Apply only newer field values, keep others
- Server-side validation: Enforce business rules (e.g., prevent modifying shipped orders)
- Conflict recording: Store both versions for manual user resolution
See examples/sync.md for complete conflict resolution implementations.
默认行为是按字段最后写入获胜。可在实现中自定义。
uploadData()核心要点:PowerSync在中赋予你完全控制权。你可以选择如何处理每个操作——接受、拒绝、合并或记录冲突。
uploadData()CrudEntry常见策略:
- 最后写入获胜(默认): 直接插入/更新每个操作
- 基于时间戳: 比较客户端与服务器时间戳,拒绝过期写入
- 字段级合并: 仅应用较新的字段值,保留其他值
- 服务器端验证: 执行业务规则(例如,禁止修改已发货订单)
- 记录冲突: 存储两个版本供用户手动解决
查看examples/sync.md了解完整的冲突解决实现。
Pattern 8: Attachment Handling
模式8:附件处理
Use in your schema and for offline-capable file upload/download.
AttachmentTableAttachmentQueuetypescript
import { AttachmentTable } from "@powersync/react-native";
import { column, Schema, Table } from "@powersync/react-native";
const users = new Table({
name: column.text,
photo_id: column.text, // References attachment ID
});
export const AppSchema = new Schema({
users,
attachments: new AttachmentTable(),
});The manages the lifecycle: local save, queued upload, synced state, automatic download on other devices, retry on failure.
AttachmentQueueSee examples/attachments.md for queue setup, upload/download handlers, and storage adapter patterns.
</patterns>
<decision_framework>
在schema中使用,并通过实现支持离线的文件上传/下载。
AttachmentTableAttachmentQueuetypescript
import { AttachmentTable } from "@powersync/react-native";
import { column, Schema, Table } from "@powersync/react-native";
const users = new Table({
name: column.text,
photo_id: column.text, // 关联附件ID
});
export const AppSchema = new Schema({
users,
attachments: new AttachmentTable(),
});AttachmentQueue查看examples/attachments.md了解队列设置、上传/下载处理器和存储适配器模式。
</patterns>
<decision_framework>
Decision Framework
决策框架
What kind of data are you storing?
|
+-> Relational data that needs offline + cloud sync?
| +-> YES -> PowerSync + SQLite (this skill)
| +-> NO -> Key-value pairs only?
| +-> YES -> Use a key-value store (not this skill)
| +-> NO -> Files/media only?
| +-> YES -> Use the filesystem
|
+-> Do you need reactive queries in React?
| +-> YES -> Use useQuery from @powersync/react
| +-> NO -> Use powersync.getAll() / get() directly
|
+-> Do you need on-device encryption?
| +-> YES -> Use @powersync/op-sqlite with SQLCipher
| +-> NO -> Use the default SQLite adapter
|
+-> Do you have file attachments?
| +-> YES -> Use AttachmentTable + AttachmentQueue
| +-> NO -> Standard schema is sufficient
|
+-> How should conflicts be resolved?
+-> Simple apps -> Last-write-wins (default)
+-> Collaborative editing -> Field-level merge or CRDTs
+-> Business-critical -> Server-side validation + conflict recording你要存储的数据类型是什么?
|
+-> 需要离线+云同步的关系型数据?
| +-> 是 -> 使用PowerSync + SQLite(本技能)
| +-> 否 -> 仅键值对?
| +-> 是 -> 使用键值存储工具(非本技能)
| +-> 否 -> 仅文件/媒体?
| +-> 是 -> 使用文件系统
|
+-> 需要在React中实现响应式查询?
| +-> 是 -> 使用@powersync/react中的useQuery
| +-> 否 -> 直接使用powersync.getAll() / get()
|
+-> 需要设备端加密?
| +-> 是 -> 使用带SQLCipher的@powersync/op-sqlite
| +-> 否 -> 使用默认SQLite适配器
|
+-> 是否有文件附件?
| +-> 是 -> 使用AttachmentTable + AttachmentQueue
| +-> 否 -> 标准schema即可
|
+-> 如何解决冲突?
+-> 简单应用 -> 最后写入获胜(默认)
+-> 协作编辑 -> 字段级合并或CRDT
+-> 业务关键 -> 服务器端验证 + 记录冲突When to Use Each Query API
各查询API的适用场景
| Scenario | API |
|---|---|
| Reactive component data | |
| Reactive with Suspense | |
| One-time fetch (no reactivity) | |
| Service/utility reads | |
| Write operations | |
| Connection status | |
| Database instance access | |
</decision_framework>
<red_flags>
| 场景 | API |
|---|---|
| 响应式组件数据 | |
| 结合Suspense的响应式查询 | |
| 一次性获取(无响应式) | |
| 服务/工具类读取 | |
| 写入操作 | |
| 连接状态 | |
| 数据库实例访问 | |
</decision_framework>
<red_flags>
RED FLAGS
注意事项(红色预警)
High Priority Issues:
- Declaring an column in schema -- PowerSync auto-creates
idasidprimary key. Declaring it causes conflicts.text - Calling for reads (SELECT) instead of
execute()/getAll()--useQuery()does not return query results in a usable formatexecute() - Forgetting -- database works locally but nothing syncs, easy to miss in development
powersync.connect(connector) - Using in React components expecting reactivity -- raw reads do not watch for changes, use
getAll()insteaduseQuery() - Missing in
transaction.complete()-- unacknowledged transactions retry indefinitely, causing duplicate uploadsuploadData()
Medium Priority Issues:
- Schema table names not matching sync rule table names -- data silently fails to sync
- Not handling returning null -- happens when auth session expires, must re-authenticate
fetchCredentials() - Storing large blobs in SQLite columns -- use for files, keep SQLite for metadata
AttachmentTable - Missing indexes on frequently queried columns -- sync queries can be slow with large datasets
- Using for booleans without consistent 0/1 values -- SQLite has no native boolean type
column.integer
Gotchas & Edge Cases:
- is a PowerSync SQL function, not a JavaScript function -- use it in SQL strings, not in JS
uuid() - stores IEEE 754 doubles -- be aware of floating-point precision for currency (use integer cents instead)
column.real - Sync rules YAML uses to access the authenticated user ID from the JWT -- not a custom function
request.user_id() - returns
getNextCrudTransaction()when the upload queue is empty -- always check before iteratingnull - with views may return
execute()even on success -- userowsAffected: 0clause for confirmationRETURNING - PowerSync supports WebSocket (default since v1.11.0) and HTTP streaming for sync -- WebSocket is recommended
- The Rust-based sync client is enabled by default since v1.29.0 -- pass to use the legacy JS client
clientImplementation: SyncClientImplementation.JAVASCRIPT - removes all local data -- use
disconnectAndClear()to stop sync while preserving local datadisconnect() - Local-only tables (set on Table options) are never synced -- useful for draft data or app state
localOnly: true
</red_flags>
<critical_reminders>
高优先级问题:
- 在schema中声明列——PowerSync会自动创建
id类型的主键text。手动声明会导致冲突。id - 使用执行读取(SELECT)而非
execute()/getAll()——useQuery()返回的查询结果格式无法直接使用execute() - 忘记调用——数据库可本地使用但无法同步,开发中容易遗漏
powersync.connect(connector) - 在React组件中使用并期望响应式——原生读取不会监听变化,需使用
getAll()useQuery() - 在中遗漏
uploadData()——未确认的事务会无限重试,导致重复上传transaction.complete()
中优先级问题:
- Schema表名与同步规则表名不匹配——数据无法同步且无提示
- 未处理返回null的情况——当认证会话过期时会发生,必须重新认证
fetchCredentials() - 在SQLite列中存储大二进制对象——使用存储文件,SQLite仅存储元数据
AttachmentTable - 未在频繁查询的列上创建索引——大数据集时同步查询会变慢
- 使用存储布尔值但未保持0/1的一致性——SQLite无原生布尔类型
column.integer
注意事项与边缘情况:
- 是PowerSync的SQL函数,而非JavaScript函数——在SQL字符串中使用,不要在JS中使用
uuid() - 存储IEEE 754双精度浮点数——注意货币的浮点精度问题(改用整数存储分)
column.real - 同步规则YAML使用从JWT获取认证用户ID——这不是自定义函数
request.user_id() - 在上传队列为空时返回
getNextCrudTransaction()——迭代前务必检查null - 使用视图时可能返回
execute()即使操作成功——使用rowsAffected: 0子句确认RETURNING - PowerSync支持WebSocket(v1.11.0起默认)和HTTP流式同步——推荐使用WebSocket
- 基于Rust的同步客户端自v1.29.0起默认启用——传递可使用旧版JS客户端
clientImplementation: SyncClientImplementation.JAVASCRIPT - 会删除所有本地数据——使用
disconnectAndClear()可停止同步并保留本地数据disconnect() - 本地专属表(在Table选项中设置)永远不会同步——适用于草稿数据或应用状态
localOnly: true
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
重要提醒
All code must follow project conventions in CLAUDE.md
(You MUST define schemas with using , , -- NEVER declare an column, PowerSync creates it automatically)
new Table({ ... })column.textcolumn.integercolumn.realid(You MUST call after to start syncing -- without it the database is local-only with no sync)
powersync.connect(connector)init()(You MUST implement both and in your backend connector -- missing either breaks the sync loop)
fetchCredentials()uploadData()(You MUST use from for reactive queries -- raw does NOT re-render on data changes)
useQuery@powersync/reactgetAll()Failure to follow these rules will cause silent sync failures, missing data, and non-reactive UIs.
</critical_reminders>
所有代码必须遵循CLAUDE.md中的项目约定
(必须使用结合、、定义schema——绝不能声明列,PowerSync会自动创建)
new Table({ ... })column.textcolumn.integercolumn.realid(必须在后调用以启动同步——否则数据库仅为本地模式,无法同步)
init()powersync.connect(connector)(必须在后端连接器中同时实现和——缺少任意一个都会破坏同步循环)
fetchCredentials()uploadData()(必须使用中的实现响应式查询——原生不会在数据变化时重新渲染)
@powersync/reactuseQuerygetAll()不遵循这些规则会导致同步静默失败、数据丢失和非响应式UI。
</critical_reminders>