@qmuse/appwrite-runtime-sdk
v1.2.0
Published
QMuse Appwrite browser runtime SDK
Maintainers
Keywords
Readme
@qmuse/appwrite-runtime-sdk
QMuse Web 应用访问 Appwrite 的运行时 SDK,提供统一的运行时初始化、用户 Session 管理、TablesDB 数据操作和文件存储能力。
安装
npm install @qmuse/appwrite-runtime-sdk [email protected]在本地仓库联调 SDK 时,可以在应用的 package.json 中引用源码包:
{
"dependencies": {
"@qmuse/appwrite-runtime-sdk": "file:../../appwrite-runtime-sdk",
"appwrite": "24.1.1"
}
}运行时上下文
Web 应用需要向 SDK 提供以下 QMuse 运行时信息:
interface QmuseRuntimeContext {
appId: string;
env: 'dev' | 'prod';
domain: string;
}| 字段 | 说明 |
| -------- | --------------------------------------------------------- |
| appId | 当前 QMuse 应用 ID |
| env | 当前运行环境 |
| domain | QMuse Runtime 服务域名,例如 https://appio-pre.qmuse.cn |
用户信息通过 getQmuseLoginUserId() 单独提供。已登录时返回 QMuse 用户 ID,未登录时返回 null。
appVersionId 不是运行时初始化或 Appwrite Custom Token 所需参数,不需要注入或传给 SDK。
React 应用
React Web 应用使用 Appwrite Web SDK 提供的 Client、Account、TablesDB 和 Storage。
1. 声明宿主环境
在项目的全局类型文件中声明 QMuse 注入的环境:
// src/types/global.d.ts
interface Window {
__MUSE__: {
appId: string;
env: 'dev' | 'prod';
domain: string;
appwrite?: {
endpoint: string;
projectId: string;
databaseId: string;
storageBucketId?: string;
};
};
__TERN__?: {
user?: {
clientUser?: {
userId?: string;
};
};
};
}2. 创建应用级单例
在统一的服务模块中创建 Runtime,并向业务代码导出所需方法:
// src/services/appwrite.ts
import {
Account,
Client,
ID,
Permission,
Query,
Role,
Storage,
TablesDB,
} from 'appwrite';
import {
createQmuseAppwriteRuntime,
type AppwriteAccountLike,
type Models,
type QmuseRuntimeContext,
} from '@qmuse/appwrite-runtime-sdk';
const client = new Client();
const account = new Account(client);
const tablesDB = new TablesDB(client);
const fileStorage = new Storage(client);
function getMuseRuntime(): QmuseRuntimeContext {
return window.__MUSE__;
}
function getQmuseLoginUserId(): string | null {
const userId = window.__TERN__?.user?.clientUser?.userId;
return typeof userId === 'string' && userId.trim() ? userId : null;
}
const runtimeAccount: AppwriteAccountLike = {
get: () => account.get(),
getSession: (input) => account.getSession(input),
deleteSession: async (input) => {
await account.deleteSession(input);
},
createAnonymousSession: () => account.createAnonymousSession(),
createSession: (input) => account.createSession(input),
};
const runtime = createQmuseAppwriteRuntime({
client,
account: runtimeAccount,
tablesDB,
fileStorage,
appwriteConfig: window.__MUSE__.appwrite,
getMuseRuntime,
getQmuseLoginUserId,
});
export const initAppwrite = runtime.init;
export const getAppwriteClients = runtime.getClients;
export const getCurrentAppwriteUser = runtime.getCurrentUser;
export const refreshAppwriteSession = runtime.refreshSession;
export const listQmuseRows = runtime.listRows;
export const getQmuseRow = runtime.getRow;
export const createQmuseRow = runtime.createRow;
export const updateQmuseRow = runtime.updateRow;
export const deleteQmuseRow = runtime.deleteRow;
export const uploadQmuseFile = runtime.uploadFile;
export const listQmuseFiles = runtime.listFiles;
export const getQmuseFile = runtime.getFile;
export const getQmuseFileViewUrl = runtime.getFileViewUrl;
export const getQmuseFilePreviewUrl = runtime.getFilePreviewUrl;
export const getQmuseFileDownloadUrl = runtime.getFileDownloadUrl;
export type { Models };
export { ID, Permission, Query, Role };每个应用只创建一个 Runtime 实例。页面、Hook 和其他业务服务统一复用该模块导出的方法。
3. 初始化应用
在渲染 React 根节点前完成初始化:
// src/main.tsx
import { createRoot } from 'react-dom/client';
import App from './App';
import { initAppwrite } from './services/appwrite';
async function bootstrap() {
await initAppwrite();
createRoot(document.getElementById('root')!).render(<App />);
}
bootstrap().catch((error) => {
console.error('Appwrite initialization failed', error);
});4. 在 React 组件中读取数据
import { useEffect, useState } from 'react';
import { listQmuseRows, Query, type Models } from './services/appwrite';
type TodoRow = Models.Row & {
title: string;
completed: boolean;
};
export function TodoList() {
const [rows, setRows] = useState<TodoRow[]>([]);
useEffect(() => {
listQmuseRows<TodoRow>({
tableId: 'todos',
queries: [Query.orderDesc('$createdAt')],
limit: 20,
})
.then((result) => setRows(result.rows))
.catch((error) => console.error('Failed to load todos', error));
}, []);
return (
<ul>
{rows.map((row) => (
<li key={row.$id}>{row.title}</li>
))}
</ul>
);
}数据操作
查询列表
const result = await listQmuseRows<TodoRow>({
tableId: 'todos',
queries: [Query.equal('completed', false)],
limit: 20,
cursor: previousPageLastRowId,
});查询单行
const row = await getQmuseRow<TodoRow>({
tableId: 'todos',
rowId: 'todo_001',
});新增
const row = await createQmuseRow<TodoRow>({
tableId: 'todos',
data: {
title: 'Read the SDK documentation',
completed: false,
},
});不传 rowId 时由 SDK 生成唯一 ID。
更新
await updateQmuseRow<TodoRow>({
tableId: 'todos',
rowId: 'todo_001',
data: { completed: true },
});删除
await deleteQmuseRow({
tableId: 'todos',
rowId: 'todo_001',
});文件存储
文件方法始终使用当前 Runtime 配置中的动态 storageBucketId,调用方不传桶 ID。没有启用文件存储时,方法会抛出错误码 QMUSE_STORAGE_NOT_INITIALIZED;用户应先通过 QMuse 对话开启文件存储。
const file = await uploadQmuseFile({
file: selectedFile,
onProgress: ({ progress }) => console.log(progress),
});
const files = await listQmuseFiles({ limit: 20 });
const metadata = await getQmuseFile({ fileId: file.$id });
const viewUrl = getQmuseFileViewUrl({ fileId: file.$id });
const previewUrl = getQmuseFilePreviewUrl({
fileId: file.$id,
width: 320,
height: 180,
});
const downloadUrl = getQmuseFileDownloadUrl({ fileId: file.$id });首版桶策略只允许任何人创建和读取文件,因此 Runtime SDK 不提供更新或删除文件 API,也不会给上传请求附加文件级权限。
公开 API
| API | 返回值 | 用途 |
| ---------------------- | ------------------------------- | ---------------------------------------- |
| init() | Promise<void> | 初始化 Runtime 和当前用户 Session |
| getClients() | Promise<QmuseAppwriteClients> | 获取已配置的客户端及 databaseId |
| getCurrentUser() | Promise<AppwriteUser> | 获取当前 Appwrite 用户 |
| refreshSession() | Promise<void> | 主动同步 QMuse 登录态和 Appwrite Session |
| listRows() | Promise<Models.RowList<Row>> | 分页查询表数据 |
| getRow() | Promise<Row> | 查询单行数据 |
| createRow() | Promise<Row> | 新增数据 |
| updateRow() | Promise<Row> | 更新数据 |
| deleteRow() | Promise<void> | 删除数据 |
| uploadFile() | Promise<Models.File> | 上传文件到当前环境的存储桶 |
| listFiles() | Promise<Models.FileList> | 分页查询文件 |
| getFile() | Promise<Models.File> | 查询文件元数据 |
| getFileViewUrl() | URL | 获取文件查看 URL |
| getFilePreviewUrl() | URL | 获取图片预览 URL |
| getFileDownloadUrl() | URL | 获取文件下载 URL |
除三个 URL 构造方法外,公开方法都是异步方法。页面和业务服务应使用 await 或显式处理 Promise 错误;URL 方法应在 init() 成功后调用。
接入约束
- 每个应用只创建一个
createQmuseAppwriteRuntime()实例。 - Web 应用向
createQmuseAppwriteRuntime()传入 Appwrite Web SDK 客户端。 getQmuseLoginUserId()只返回有效的用户 ID 或null。- 业务代码通过 Runtime 的 CRUD 和文件方法访问云服务,不直接拼接 Appwrite HTTP 接口或存储桶 ID。
SDK 开发
tnpm install
npm run lint
npm test
npm run build发布
先在 package.json 中设置未发布的语义化版本,然后执行:
npm run publish:public发布脚本默认使用 dev tag,也可以选择 latest。正式发布前应确保 lint、测试和构建全部通过。
