ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

SpacetimeDB 实时聊天应用实战:基于 TypeScript 模块与 React 构建 Discord 风格全功能聊天室

SpacetimeDB 实时聊天应用实战:基于 TypeScript 模块与 React 构建 Discord 风格全功能聊天室 SpacetimeDB 实时聊天应用实战基于 TypeScript 模块与 React 构建 Discord 风格全功能聊天室【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB本文以仓库 tools/llm-oneshot/apps/chat-app 下的chat-app-20260102-170500示例为骨架系统拆解如何用 SpacetimeDB 的 TypeScript 服务端模块 React/Vite 客户端实现一套包含公开房间、私密房间、私信DM、打字指示、已读回执、未读计数、定时消息、阅后即焚、Emoji 表情回应、消息编辑与历史、消息线程、富状态在线状态与管理员权限的完整聊天应用。读完本文你将掌握 SpacetimeDB 的表定义、Reducer 编写、定时调度Scheduled Reducer、客户端订阅绑定生成与 React 响应式渲染的完整链路并能独立复现、部署并二次扩展这套聊天应用。一、项目概览12 张表 20 余个 Reducer 的全栈实时架构该示例应用是一个以 SpacetimeDB 为后端的全栈实时聊天系统由三部分组成后端模块SpacetimeDB TypeScript 模块位于 backend/spacetimedb核心是 schema.ts表定义约 200 行与 index.ts全部业务 Reducer约 800 行前端客户端React Vite TypeScript位于 client/src入口 main.tsx 建立连接App.tsx 承载全部交互逻辑部署编排docker-compose.yml 提供一键启动 SpacetimeDB 独立服务的容器配置。后端与客户端均以spacetimedb: ^1.1.1作为唯一运行时 SDK 依赖见 backend/package.json 与 client/package.json前端另依赖 React 18、Vite 6 与 TypeScript 5。仓库附带的 GRADING_RESULTS.md 显示该实现一次性通过编译、可运行并在 12 项功能测评中拿到 32.5/36 分约 90.3%。该示例对应的开发规范沉淀在 prompts/language/typescript-spacetime.md后端严格限定在backend/spacetimedb/内编写 TypeScript 模块客户端限定在client/src/内界面采用 SpacetimeDB 官方品牌色暗色主题主色#4cf490绿色、辅助#a880ff紫色、背景#0d0d0e。二、从零部署Docker 与本地两种启动路径README 提供了两条等价部署路线详见 README.md 的 Deployment 一节。2.1 方式一Docker 启动 SpacetimeDB推荐# 1. 启动 SpacetimeDB 容器 docker-compose up -d # 2. 等待容器就绪后将本地 CLI 指向该服务 spacetime server add docker http://localhost:3000 --no-fingerprint spacetime server set-default docker # 3. 安装后端依赖并发布模块--clear-database 清空旧数据echo y 确认 cd backend/spacetimedb npm install cd ../.. echo y | spacetime publish chat-app --clear-database --module-path backend/spacetimedb # 4. 生成客户端类型绑定 spacetime generate --lang typescript --out-dir client/src/module_bindings --module-path backend/spacetimedb # 5. 安装并启动客户端 cd client npm install npm run dev对应的 docker-compose.yml 内容如下version: 3.8 services: spacetimedb: image: clockworklabs/spacetimedb-standalone:latest ports: - 3000:3000 volumes: - spacetimedb-data:/stdb volumes: spacetimedb-data:要点说明容器镜像clockworklabs/spacetimedb-standalone:latest是官方独立版服务宿主3000端口映射到容器内3000数据卷spacetimedb-data:/stdb持久化数据库文件重启容器数据不丢失--no-fingerprint跳过 TLS 指纹校验适用于本地 http 端点。2.2 方式二本地 SpacetimeDB 进程若已安装 SpacetimeDB CLI可直接使用本机进程# 确保 SpacetimeDB 已在运行 spacetime start # 之后与 Docker 方式相同安装依赖、发布模块、生成绑定、启动前端 cd backend/spacetimedb npm install cd ../.. echo y | spacetime publish chat-app --clear-database --module-path backend/spacetimedb spacetime generate --lang typescript --out-dir client/src/module_bindings --module-path backend/spacetimedb cd client npm install npm run dev2.3 部署链路的关键语义结合仓库源码可将上述命令串映射到 SpacetimeDB 的完整开发闭环spacetime publish把 backend/spacetimedb/src/index.ts 编译并注册为名为chat-app的模块spacetime generate基于模块的 schema 生成前端可直接 import 的类型化绑定client/src/module_bindings前端 App.tsx 中import { DbConnection, tables } from ./module_bindings即来自该产物前端 main.tsx 通过DbConnection.builder().withUri(ws://localhost:3000).withModuleName(chat-app)连接服务并在onConnect回调中调用conn.subscriptionBuilder().subscribeToAllTables()订阅全部表实现服务端数据变更实时推送到客户端。三、数据库模型12 张表背后的设计意图schema 全部定义在 schema.ts覆盖用户—房间—消息—互动四层语义。下表汇总均为public: true的公开表表名用途关键字段user用户档案与在线状态identity主键、name、status、lastActive、onlineroom聊天室公开/私密/DMid自增主键、name、creatorId、isPrivate、isDmroom_member房间成员与角色roomId、userId、roleadmin/member、joinedAtmessage消息支持线程与过期roomId、senderId、content、parentId、expiresAttyping_indicator实时打字状态roomId、userId、startedAtread_receipt每用户每房间的已读位置roomId、userId、lastReadMessageId、readAtreaction消息表情回应messageId、userId、emojiedit_history消息编辑历史messageId、oldContent、editedAtroom_invitation私密房间邀请roomId、inviterId、inviteeId、statusscheduled_message定时发送任务scheduledAtt.scheduleAt()、roomId、senderId、contentephemeral_cleanup阅后即焚清理任务scheduledAt、messageIdtyping_cleanup打字状态过期清理任务scheduledAt、typingId几个值得注意的建模细节身份体系复用 SpacetimeDB 内建 Identityuser.identity使用t.identity()类型并作为主键room.creatorId、senderId等同型引用。客户端连接后由服务端自动分配身份无需自建登录系统这一设计选择在 GRADING_RESULTS.md 的 Architecture Notes 中有明确记录索引即查询路径room建有by_creator索引room_member建有by_room、by_usermessage建有by_room、by_sender、by_parentreaction、edit_history、room_invitation等也各自声明索引代码中大量使用ctx.db.roomMember.by_room.filter(roomId)、ctx.db.message.by_room.filter(roomId)走索引过滤三张任务表承载定时逻辑scheduled_message、ephemeral_cleanup、typing_cleanup都声明了scheduled: reducer名属性把表与定时执行的 Reducer绑定这是 SpacetimeDB 调度机制的核心用法详见第五节。四、业务 Reducer 全景从用户、房间到权限backend/spacetimedb/src/index.ts 以export const spacetimedb schema(...)将 12 张表注册进模块随后用spacetimedb.reducer(name, { 参数schema }, (ctx, args) {...})定义业务逻辑。一个典型示例send_messagespacetimedb.reducer( send_message, { roomId: t.u64(), content: t.string(), parentId: t.u64().optional() }, (ctx, { roomId, content, parentId }) { const trimmed content.trim(); if (!trimmed || trimmed.length 2000) { throw new SenderError(Message must be 1-2000 characters); } const room ctx.db.room.id.find(roomId); if (!room) throw new SenderError(Room not found); // 校验成员资格… // 校验 parentId 是否属于同一房间线程… ctx.db.message.insert({ id: 0n, roomId, senderId: ctx.sender, content: trimmed, createdAt: ctx.timestamp, editedAt: undefined, parentId, expiresAt: undefined, }); // 同时清除该用户的打字指示… } );这里体现了 SpacetimeDB Reducer 的共性模式参数用t.*类型构造器声明、ctx.sender取调用者身份、ctx.timestamp取当前时间、SenderError向客户端回传可读错误、主键为autoInc的表插入时传0n占位。按功能域梳理全部 Reducer4.1 生命周期与用户域clientConnected/clientDisconnected连接建立时若user.identity表中有该身份则置online: true否则自动插入一条新用户记录name暂为undefined断开时置online: false并清理其打字指示。set_name昵称 1–50 字符校验更新name与lastActive。set_status仅允许[online, away, dnd, invisible]四值。heartbeat前端每 30 秒调用一次见 App.tsx持续刷新lastActive支撑上次在线时间展示。4.2 房间与私密房间域create_room房间名 1–100 字符插入room后创建者自动以admin角色写入room_member。join_room/leave_room公开房间可直接加入私密房间拒绝加入必须走邀请已加入/未加入均返回SenderError。create_dm按身份查找目标用户若两人间已存在 2 人 DM 则报错否则创建isDm: true的私密房间并同时把双方加入为admin。invite_to_room仅管理员可对私密房间发起邀请邀请人、被邀请人、房间三者幂等去重。respond_to_invitation仅被邀请人本人可响应pending → accepted/declined接受则写入room_member。4.3 消息与互动域send_message2000 字符上限校验房间与成员身份支持parentId线程回复发送后清除打字状态。send_ephemeral_messagedurationSeconds限定 10–3600 秒expiresAt ctx.timestamp durationSeconds * 1_000_000微秒同时向ephemeral_cleanup写入一条ScheduleAt.time(expiresAtMicros)调度任务。edit_message/delete_message编辑仅限本人历史写入edit_history删除时级联清理该消息的 reactions 与 edit_history管理员也可删他人消息。toggle_reaction表情白名单[,❤️,,,,,,]已点则取消toggle 语义。start_typing/stop_typing写入/删除typing_indicatorstart_typing同时调度 5 秒后的清理任务。mark_read按用户/房间更新lastReadMessageId仅在消息更新时才推进。promote_to_admin/kick_user仅管理员可执行kick_user禁止踢自己。4.4 定时调度 ReducerScheduled Reducersend_scheduled_message收到ScheduledMessage.rowType参数到点执行发送前复查发送者是否仍是房间成员是才真正插入消息——这保证了退房后定时消息不再发出。cleanup_ephemeral_message到点后级联删除消息的 reactions、edit_history 并删除消息本体实现真正的阅后即焚。cleanup_typing_indicator到点后检查指示年龄是否超过 4 秒即距今超过 5 秒调度点 1 秒容差再删除避免误删刚刷新的指示。五、定时能力深挖SpacetimeDB 的 Scheduler 机制本应用把定时消息、阅后即焚、打字指示过期三类能力统一建立在 SpacetimeDB 的定时调度Scheduler之上是最具 SpacetimeDB 特色的部分。声明方式见 schema.tsexport const ScheduledMessage table( { name: scheduled_message, scheduled: send_scheduled_message, // 绑定到定时 Reducer }, { scheduledId: t.u64().primaryKey().autoInc(), scheduledAt: t.scheduleAt(), // 调度时间字段 roomId: t.u64(), senderId: t.identity(), content: t.string(), } );触发方式在任意业务 Reducer 中向该表插入行scheduledAt使用ScheduleAt.time(microsSinceUnixEpoch)指定绝对时间见schedule_message、send_ephemeral_message、start_typing三处调用。到达时间后模块自动以该行数据为参数调用绑定 Reducer。业务闭环示例定时消息从创建到发送schedule_message校验未来时间 房间成员资格向scheduled_message插入ScheduleAt.time(sendAtMicros)前端 App.tsx 的 Scheduled Messages 面板展示本人待发消息提供cancel_scheduled_message取消取消时校验仅限本人到点后send_scheduled_message收到{ arg: ScheduledMessage.rowType }复查成员资格后写入message表前端useTable(tables.message)自动收到增量并实时上屏。阅后即焚的链路同理send_ephemeral_message计算expiresAt并调度cleanup_ephemeral_message客户端则通过消息的expiresAt字段渲染Xm remaining倒计时见 App.tsx到点后后端删除、前端表更新消息从所有客户端消失。六、客户端实现React 响应式订阅与完整交互6.1 连接建立与全局订阅main.tsx 通过DbConnection.builder()链式配置 URIws://localhost:3000、模块名chat-app、本地缓存 token并在onConnect中把连接与身份挂到window.__db_conn/window.__my_identity供 App 轮询获取同时subscribeToAllTables()一次性订阅全部表onConnectError处理Unauthorized/401时清除 token 并刷新页面。6.2 useTable 驱动的实时 UIApp.tsx 用useTable(tables.xxx)声明式订阅 10 张表返回值即为响应式数组——服务端任何 insert/update/delete 都会触发 React 重渲染无需手动刷新const [users, usersLoading] useTable(tables.user); const [rooms, roomsLoading] useTable(tables.room); const [roomMembers, membersLoading] useTable(tables.roomMember); const [messages, messagesLoading] useTable(tables.message); const [typingIndicators] useTable(tables.typingIndicator); const [readReceipts] useTable(tables.readReceipt); const [reactions] useTable(tables.reaction); const [editHistory] useTable(tables.editHistory); const [invitations] useTable(tables.roomInvitation); const [scheduledMessages] useTable(tables.scheduledMessage);调用 Reducer 同样走绑定conn.reducers.sendMessage({...})、conn.reducers.markRead({...})等参数名由后端参数 schema 自动生成camelCase。重要交互的实现要点未读计数getUnreadCount依据readReceipts中本人lastReadMessageId统计本房间内id lastReadId且非本人发送的消息数侧边栏渲染unread-badge切到房间时自动调用markRead推进已读位置App.tsx已读回执getMessageReadBy聚合所有lastReadMessageId 当前消息的房间成员用户名消息下方显示 Seen by X, Y打字指示输入框onChange触发startTyping节流 2 秒一次3 秒无输入后stopTyping服务端typing_indicator表变更实时驱动 X is typing... 文案消息线程parentId为空的是顶层消息getThreadReplies过滤出回复并在右侧线程面板渲染发送时携带parentId: showThread表情回应toggle_reaction实现 toggleUI 按 emoji 分组计数并高亮自己的回应悬停显示谁点的编辑与历史本人消息可编辑(edited)标记 按钮弹出编辑历史弹窗权限管理isAdmin判定当前用户角色管理员对成员弹出 Make Admin / Kick 菜单私密房间头部提供 Invite 按钮邀请与 DM 分别通过模态框完成。6.3 前端目录与工程化client/ ├── src/ │ ├── App.tsx # 主应用单文件承载全部交互逻辑 │ ├── main.tsx # 入口连接构建 SpacetimeDBProvider │ ├── index.css # 暗色主题样式SpacetimeDB 品牌色 │ └── module_bindings/ # spacetime generate 生成的类型化绑定 ├── index.html ├── package.json # scripts: dev / build / preview ├── tsconfig.json └── vite.config.tspackage.json 中dev: vite直接提供开发服务器build: tsc vite build先做类型检查再产出静态资源。七、功能测评对照已知缺口与扩展方向仓库 GRADING_RESULTS.md 对 12 项要求内功能逐条打分可作为理解各功能完整度的权威参考满分项12 项中 8 项得 3/3基础聊天、打字指示、已读回执、未读计数、阅后即焚、表情回应、消息编辑与历史、消息线程、私密房间与 DM扣分项定时消息2/3功能可用但待发消息面板只在已有定时任务时出现且scheduled_message表默认私有导致客户端不一定能订阅全部行实时权限2.5/3踢人、晋升管理员正常但ban封禁未实现富在线状态2.5/3手动状态切换与心跳正常但长时间不活动自动置为 away未实现未纳入范围0/3房间活跃度徽标、草稿跨端同步、匿名身份迁移三项不在提示要求内。这些已知缺口即为二次开发的最佳切入点例如可在heartbeat中增加lastActive与当前时间的差值判断自动置为awaykick_user之上可再加一张ban表与banned校验。八、总结这个示例完整展示了 SpacetimeDB TypeScript SDK 的核心编程模型表结构即数据库、Reducer 即服务端逻辑、Scheduled Reducer 即定时任务、spacetime generate产物即类型安全的前后端契约、useTable即实时 UI 的数据源。配合 Docker 一键部署即可获得一个功能密度不亚于商业 IM 的实时聊天应用。无论你是要复刻这套聊天功能还是研究 SpacetimeDB 模块化后端的最佳实践chat-app-20260102-170500 目录下的 schema.ts 与 index.ts 都是值得通读的活教材。【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表