- 后端
- 数据库
【免费下载链接】instant
Instant is the best backend for AI-coded apps. You get auth, permissions, storage, presence, and streams — everything you need to ship apps your users will love.
本文以仓库中的 SolidJS + Vite 进阶示例 为骨架,系统讲解一个基于 SolidJS、Vite 与 InstantDB 的完整实战项目:包括依赖安装、开发服务器、生产构建与静态部署流程,并深入源码剖析其认证、响应式数据查询、事务写入、在线 Presence、房间话题(Topics)聊天与打字指示器等进阶能力。读完本文,你将掌握如何从零跑通并理解一个"开箱即用的实时全栈"SolidJS 应用,以及每个核心能力对应的 InstantDB API 与代码实现位置。
一、示例项目一览与目录结构
solidjs-vite-advanced是仓库examples目录下基于 Solid CLI 生成的进阶示例,与基础版 examples/solidjs-vite 相比,它一次性演示了 InstantDB 的多数核心能力:
- 邮箱魔法码(Magic Code)认证与登录状态机;
useQuery响应式查询与transact事务写入;- Rooms 房间机制下的 Presence(在线状态)与 Topics(实时消息);
- 打字指示器、连接状态监控与本地设备 ID 等进阶 API。
完整目录结构如下:
examples/solidjs-vite-advanced/ ├── index.html # Vite 入口 HTML(挂载 #root) ├── package.json # 脚本与依赖定义 ├── vite.config.ts # Vite 配置(含 Tailwind v4 插件) ├── tsconfig.json # TypeScript 配置 ├── src/ │ ├── index.tsx # 应用入口(render + solid-devtools) │ ├── App.tsx # 根组件与认证状态机 │ ├── instant.schema.ts # 数据模型(entities / links / rooms) │ ├── instant.perms.ts # 权限规则(当前为空规则) │ ├── lib/db.ts # InstantDB 客户端初始化 │ └── components/ │ ├── Login.tsx # 魔法码登录 │ ├── StatusBar.tsx # 连接状态 / 本地 ID │ ├── PresenceBar.tsx # 在线成员列表 │ ├── TodoList.tsx # 响应式查询 + 事务 │ └── ChatPanel.tsx # 实时聊天 + 打字指示器依赖方面,package.json 同时引入了@instantdb/core(数据建模与查询内核)、@instantdb/solidjs(SolidJS 适配层)与solid-js本体,构建工具链为 Vite 7 +vite-plugin-solid+ Tailwind CSS v4(通过@tailwindcss/vite插件接入),并启用了solid-devtools开发调试。其中@instantdb/solidjs与@instantdb/core的源码分别位于 client/packages/solidjs 与 client/packages/core,是本文后续 API 讲解的底层依据。
二、环境准备与依赖安装
示例项目(README 及锁文件说明)默认使用 pnpm 维护依赖,这也是仓库中同时存在pnpm-lock.yaml的原因。README 明确说明:锁文件只是历史遗留,任何包管理器均可正常工作;一旦克隆模板后,该锁文件可以安全移除。
若依赖版本需要更新,README 给出的维护命令是:
pnpm up -Lri其中-L表示升级到最新版本、-r表示递归处理工作区、-i为交互式选择模式。日常安装依赖则三种包管理器任选其一:
npm install # 或 pnpm install # 或 yarn install安装完成后,还需要为本项目提供 InstantDB 应用 ID。客户端初始化代码 db.ts 通过import.meta.env.VITE_INSTANT_APP_ID读取该值,因此需要在项目根目录创建.env文件并写入:
VITE_INSTANT_APP_ID=你的应用ID该值来自 Instant 控制台创建应用后生成的app-id,Vite 会将其注入到import.meta.env供客户端使用。
三、开发运行:dev 与热更新
README 与 package.json 中定义了四个脚本:
| 命令 | 作用 |
|---|---|
npm run dev(或npm start) | 启动 Vite 开发服务器,支持热更新 |
npm run build | 生产构建,产物输出到dist目录 |
npm run serve | 本地预览dist构建产物(vite preview) |
在项目目录下执行:
npm run devVite 会启动开发服务器,浏览器打开http://localhost:3000即可看到应用。开发模式下对源码的编辑会即时反映到页面(热模块替换)。需要留意的是,index.tsx 在开发环境会校验#root元素是否存在——如果index.html中漏写了该挂载点或id拼写错误,会直接抛出 "Root element not found" 的明确报错。
另外,开发模式下入口还引入了solid-devtools(index.tsx),配合浏览器扩展可对 Solid 响应式系统进行组件树与信号调试。
四、生产构建与静态部署
执行:
npm run buildVite 会以生产模式打包 Solid 应用:代码被压缩(minified)、产物文件名带内容哈希(hashes),并输出到项目根目录的dist文件夹。构建完成后,可用npm run serve(即vite preview)在本地验证产物行为。
README 明确指出部署方式:将dist目录部署到任意静态托管服务即可(如 Netlify、Surge、Now 等主流静态平台)。由于应用是纯前端 + InstantDB 托管后端的架构,无需自建服务器,静态托管天然适合。这也是 InstantDB 类 BaaS 应用最常见的上线路径。
五、项目骨架:入口与认证状态机
应用入口 index.tsx 非常简单:引入全局样式、注册 solid-devtools、然后render(() => <App />, root)。
根组件 App.tsx 展示了 SolidJS 处理异步认证状态的经典模式——用<Switch>/<Match>组合db.useAuth()返回的三种状态做分支渲染:
const auth = db.useAuth(); <Switch> <Match when={auth().isLoading}> <p>Loading...</p> </Match> <Match when={auth().error}> <p>Auth error: {auth().error?.message}</p> </Match> <Match when={auth().user}> <AuthenticatedApp /> </Match> <Match when={true}> <Login /> </Match> </Switch>未登录时渲染<Login />;登录成功(auth().user存在)后进入AuthenticatedApp,展示用户邮箱、退出按钮,以及StatusBar、PresenceBar、TodoList、ChatPanel四个功能面板(App.tsx)。这种"信号驱动 + 模式匹配"的写法正是 SolidJS 细粒度响应式的直观体现。
六、初始化 InstantDB 客户端
所有功能的入口是 db.ts,它只做了三件事:
import { init, id, tx } from "@instantdb/solidjs"; import schema from "../instant.schema"; export { id, tx }; export const db = init({ appId: import.meta.env.VITE_INSTANT_APP_ID!, schema, useDateObjects: true, }); export const chatRoom = db.room("chat", "lobby");要点解析:
init是@instantdb/solidjs暴露的初始化函数,内部与@instantdb/core的客户端实现对接;appId来自环境变量,!断言保证非空;schema传入上一步定义的数据模型,使查询获得完整的 TypeScript 类型推导;useDateObjects: true让时间字段以Date对象形式返回(默认可能为时间戳数字);id与tx是事务写入的辅助工具:id()生成新的随机实体 ID,tx.xxx[id()]构造写入语句;db.room("chat", "lobby")声明一个房间引用——chat对应 schema 中定义的房间类型,lobby是具体房间名,后续 Presence 与 Topics 都基于它。
七、数据建模与权限规则
instant.schema.ts 使用@instantdb/core的i.schema定义了完整的类型安全数据模型:
实体(entities):
const _schema = i.schema({ entities: { $files: i.entity({ path: i.string().unique().indexed(), url: i.string(), }), $users: i.entity({ email: i.string().unique().indexed().optional(), imageURL: i.string().optional(), type: i.string().optional(), }), todos: i.entity({ createdAt: i.number(), done: i.boolean(), text: i.string(), }), }, ... });$files、$users是 Instant 内建实体(文件与用户),todos是应用自定义实体。i.string().unique().indexed()这类链式约束会同时作用于服务端校验与本地类型;.optional()表示字段可缺省。
关联(links):
links: { $usersLinkedPrimaryUser: { forward: { on: "$users", has: "one", label: "linkedPrimaryUser", onDelete: "cascade" }, reverse: { on: "$users", has: "many", label: "linkedGuestUsers" }, }, },这里定义了一条 $users 到自身的关联:正向linkedPrimaryUser(一对一,删除时级联),反向linkedGuestUsers(一对多)。
房间(rooms):
rooms: { chat: { presence: i.entity({ nickname: i.string(), color: i.string(), }), topics: { chat: i.entity({ message: i.string(), sender: i.string(), timestamp: i.number(), }), }, }, },rooms.chat.presence描述加入该房间的用户携带的在线状态数据(昵称、颜色);rooms.chat.topics.chat描述该房间内名为chat的话题消息结构(内容、发送者、时间戳)。这与 PresenceBar.tsx 与 ChatPanel.tsx 的字段一一对应。
模型末尾通过type _AppSchema = typeof _schema; interface AppSchema extends _AppSchema {}导出AppSchema,为查询提供更友好的 IntelliSense 提示。
权限规则:instant.perms.ts 当前是空规则模板(satisfies InstantRules),文件内注释给出了典型写法示例,例如对posts实体允许所有人查看、仅拥有者可增删改:
posts: { allow: { view: "true", create: "isOwner", update: "isOwner", delete: "isOwner", }, bind: {"isOwner": "auth.id != null && auth.id == data.ownerId"}, },权限规则在服务端强制执行,是生产环境必须补齐的一环。
八、实现邮箱魔法码认证
Login.tsx 完整演示了 InstantDB 的两步魔法码认证流程。
第一步:发送验证码。调用db.auth.sendMagicCode({ email }),成功后记录目标邮箱并切换界面(Login.tsx):
await db.auth.sendMagicCode({ email: email() }); setSentTo(email());第二步:校验验证码登录。调用db.auth.signInWithMagicCode({ email, code })完成登录(Login.tsx):
await db.auth.signInWithMagicCode({ email: sentTo(), code: code(), });两步都包裹在 try/catch 中,错误信息取自err.body?.message || err.message。界面用<Show when={sentTo()}>在两个表单(输入邮箱 → 输入验证码)间切换,并提供"Use a different email"返回重试。登录态一旦建立,根组件App.tsx的auth().user信号会自动更新并切换到主界面,无需手动刷新。
九、响应式查询与事务写入:TodoList
TodoList.tsx 是 InstantDB 核心数据能力的浓缩演示。
响应式查询:const state = db.useQuery({ todos: {} })返回一个 Solid 信号,state().data.todos、state().isLoading、state().error会随远端数据自动更新(TodoList.tsx)。本地列表按createdAt升序排序后渲染。
事务写入:新增、切换完成态、删除分别对应三种transact用法(TodoList.tsx):
// 新增:id() 生成新 ID,update 写入字段 db.transact( tx.todos[id()].update({ text, done: false, createdAt: Date.now(), }), ); // 更新:按已有 ID 更新字段 db.transact(tx.todos[todoId].update({ done: !currentDone })); // 删除 db.transact(tx.todos[todoId].delete());id()与tx均从 db.ts 再导出,保证与客户端使用同一套底层实现。事务是离线优先的:即便网络瞬时不可用,写入也会在本地排队并在恢复后同步。
一次性查询:面板底部还演示了db.queryOnce({ todos: {} })——与useQuery的持续订阅不同,queryOnce只取一次快照并返回 Promise,适合按钮触发的一次性读取(TodoList.tsx)。
十、多人实时会话:Presence 在线状态
PresenceBar.tsx 展示如何让进入同一房间的客户端彼此感知。
同步自己的状态:db.rooms.useSyncPresence(chatRoom, { nickname, color })把当前用户信息(邮箱前缀昵称 + 由邮箱哈希决定的头像颜色)发布到房间(PresenceBar.tsx)。
订阅他人状态:db.rooms.usePresence(chatRoom, { keys: ["nickname", "color"] })返回peers对象,遍历后渲染为彩色昵称徽章;presence().isLoading用于加载态提示(PresenceBar.tsx)。所有进入chat/lobby房间的客户端都会实时出现在彼此的在线列表中,客户端离线后其 Presence 自动过期,无需手动清理。
十一、实时聊天:Topics 消息与打字指示器
ChatPanel.tsx 是房间机制最完整的用例,包含三个配合使用的 API。
发布消息(Topic):const publishChat = db.rooms.usePublishTopic(chatRoom, "chat")向chat话题发布消息(ChatPanel.tsx)。值得注意的语义是Topics 不会回显给发送者自己,因此发送方需在本地追加自己的消息,代码注释与实现都明确标注了这一点(ChatPanel.tsx)。
订阅消息:db.rooms.useTopicEffect(chatRoom, "chat", (event) => {...})注册回调,收到远端消息后追加到本地信号并滚动到底部(ChatPanel.tsx):
db.rooms.useTopicEffect(chatRoom, "chat", (event, _peer) => { setMessages((prev) => [...prev, event as ChatMessage]); scrollToBottom(); });打字指示器:db.rooms.useTypingIndicator(chatRoom, "chat-input", { timeout: 2000, stopOnEnter: true })监听输入框事件,自动向房间广播"正在输入"状态,timeout控制多久未输入后自动取消,stopOnEnter表示回车发送时立即停止(ChatPanel.tsx)。typing.active()返回正在输入的其他用户列表,配合typing.inputProps.onKeyDown/onBlur挂载到输入框,即可在界面下方显示"xxx is typing..."提示(ChatPanel.tsx)。
十二、连接状态与本地设备标识
StatusBar.tsx 展示了三个诊断类 API。
连接状态:db.useConnectionStatus()返回实时连接状态,代码中定义了五种颜色映射(StatusBar.tsx):
| 状态值 | 含义 |
|---|---|
connecting | 正在连接(黄色) |
opened | 连接已建立(蓝色) |
authenticated | 已认证(绿色) |
closed | 连接已关闭(灰色) |
errored | 连接出错(红色) |
本地设备 ID:db.useLocalId("device")以信号形式提供持久化的本地标识(刷新后保持不变),同文件还演示了异步版本await db.getLocalId("device")(StatusBar.tsx)。
当前认证信息:await db.getAuth()以 Promise 方式获取当前登录用户,与useAuth()的信号式 API 互补,适合在事件处理器中按需读取(StatusBar.tsx)。
十三、延伸学习与相关源码位置
- 本示例使用的
@instantdb/solidjs适配层源码位于 client/packages/solidjs,其底层查询、事务与建模内核在 client/packages/core,useQuery、transact、i.schema等 API 的实现与测试都可在这两个包内找到; - 更基础、聚焦单一功能的对照示例可看 examples/solidjs-vite;
- 权限规则的完整语法与绑定(bind)示例见 instant.perms.ts 内的注释说明。
结语
solidjs-vite-advanced虽然只是一个模板级示例,却覆盖了 InstantDB 生产应用的核心拼图:类型安全的数据建模、魔法码认证、响应式查询与离线优先事务、基于 Rooms 的 Presence 与 Topics 实时会话、打字指示器以及连接诊断 API。结合 README 的安装、开发、构建与静态部署四步流程,你可以直接以此为蓝本,在 SolidJS 生态中快速搭建带实时协作能力的完整应用。
- 后端
- 数据库
【免费下载链接】instant
Instant is the best backend for AI-coded apps. You get auth, permissions, storage, presence, and streams — everything you need to ship apps your users will love.
相关推荐
DB-GPT 定时任务实战:把一次数据分析对话冻结成可复放的 Cron 任务
DB GPT 定时任务实战:把一次数据分析对话冻结成可复放的 Cron 任务 在 DB GPT 中,一次对话跑完的数据分析报告往往“看完即弃”。Schedule
后端数据库使用 @instantdb/react 构建实时多人 React 应用:从实时聊天到多人光标完整实战指南
使用 @instantdb/react 构建实时多人 React 应用:从实时聊天到多人光标完整实战指南 @instantdb/react 是 Instant(
后端数据库基于 InstantDB 与 SolidJS + Vite 构建实时 Todo 应用:示例项目完整解析
基于 InstantDB 与 SolidJS + Vite 构建实时 Todo 应用:示例项目完整解析 InstantDB 是一款面向 AI 编码时代应用的后端
后端数据库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考