ElectricSQL TodoMVC 示例应用完全指南:从本地启动到读写分离架构剖析 📅 发布时间:2026/9/15 19:20:14 👁 浏览次数: ElectricSQL TodoMVC 示例应用完全指南从本地启动到读写分离架构剖析【免费下载链接】electricThe agent platform built on sync.项目地址: https://gitcode.com/GitHub_Trending/el/electric本指南以 examples/todo-app/README.md 为核心完整讲解 Electric 仓库中经典 TodoMVC 示例应用的启动步骤、工程结构与实现原理。你将掌握如何在 pnpm monorepo 中安装构建、通过 Docker Compose 一键拉起 Postgres 与 Electric 后端、执行数据库迁移并理解示例如何用useShape实现响应式数据订阅、用 Express 服务端完成写入的读-写分离架构。示例概览一个经典 TodoMVC 应用examples/todo-app是一个使用 ElectricSQL 开发的经典 TodoMVC 应用属于 Electric 开源仓库The agent platform built on sync中的官方示例。从 package.json 可以看到它的技术栈定位Somewhat opinionated starter for ElectricSQL with Vite, and React Router即一个面向 ElectricSQL 的、带有鲜明技术选型倾向的起步模板核心依赖包括electric-sql/client与electric-sql/react均为workspace:*链接到仓库内源码分别提供底层同步客户端与 React 的useShape响应式 HookVite React 18 React Router 6前端工程与路由骨架Radix UI Themes FontsourceUI 组件与字体Express pg zod body-parser corsNode.js 写代理服务负责接收前端写请求并直连 PostgressstServerless Stack云部署编排详见 sst.config.ts。整个示例演示了 ElectricSQL 的典型读-写分离用法读路径由前端通过 Electric Shape 订阅实时同步写路径则由后端 API 直连数据库完成数据库变更再通过 WAL 同步回所有订阅客户端。前置条件作为 pnpm workspace 一部分运行该示例是 Electric monorepo 的组成部分必须放在 pnpm-workspace.yaml 定义的工作区上下文中构建和运行不能单独安装。因为 package.json 中的依赖声明为electric-sql/client: workspace:*、electric-sql/react: workspace:*这类 workspace 协议依赖只有在 monorepo 根目录执行pnpm install时才能被正确解析并软链接到packages/下的本地源码。运行前需要准备的环境包括Node.js 与 pnpm用于工作区安装与构建Docker Docker Compose示例后端服务Postgres 与 Electric sync service通过 Docker Compose 启动可选的PostgreSQL 客户端工具db:migrate脚本通过pg-migrations见 devDependencies应用迁移无需手工连接数据库。安装与构建整个工作区按照 README 的标准流程首先进入 monorepo 根目录示例位于examples/todo-app其上一级两级即为仓库根cd ../../然后安装并构建所有 workspace 包与示例pnpm install pnpm run -r buildpnpm install会依据 pnpm-workspace.yaml 解析全部 workspace 包包括packages/sync-service、packages/typescript-client、packages/react-hooks等并安装依赖pnpm run -r build则按依赖顺序递归构建所有需要构建的包与示例确保electric-sql/client、electric-sql/react等 workspace 依赖的产物dist就绪前端示例才能在后续构建中被正确引用。构建完成后回到示例目录cd examples/todo-app启动后端服务pnpm backend:up在示例目录执行pnpm backend:up这一命令会串联完成两件事见 package.json 的 scripts 定义backend:up: PROJECT_NAMEtodo-app-example pnpm -C ../../ run example-backend:up pnpm db:migrate设置PROJECT_NAMEtodo-app-example环境变量后调用仓库根目录的example-backend:up脚本后端容器就绪后立即执行db:migrate应用数据库迁移。根目录脚本与 Docker Compose仓库根 package.json 中定义了三个相关脚本example-backend:up: pnpm example-backend:down pnpm example-backend:just_up, example-backend:just_up: dotenv -e .env.dev -- docker compose -f ./.support/docker-compose.yml up -d, example-backend:down: dotenv -e .env.dev -- docker compose -f .support/docker-compose.yml down --volumes也就是说backend:up实际执行的是down --volumes停止并删除容器与卷再up -d后台启动。README 特别提醒该命令总会停止并删除其他示例后端容器挂载的卷这是刻意为之用于保证示例每次都以干净的数据库和干净的磁盘状态启动。因此在同一台机器上并行运行多个示例时需留意backend:up会互相清理。docker-compose.yml 服务详解容器定义位于 .support/docker-compose.yml包含两个服务postgres数据库镜像postgres:16-alpine数据库名electric用户/密码postgres/password端口映射54321:5432将容器内 Postgres 暴露到宿主机 54321 端口避免与本地常见 5432 冲突使用./postgres.conf见 .support/postgres.conf作为配置按 README 与 WAL 相关说明该配置为 Electric 启用了所需的wal_levellogical等复制设置数据目录与/tmp挂载为tmpfs内存文件系统这正是每次启动都是干净数据库的关键容器重启即数据清空配合down --volumes保证磁盘干净。backendElectric sync service镜像electricsql/electric:canarycanary 为每日构建的开发镜像对应 packages/sync-service 源码通过DATABASE_URL指向同 Compose 网络内的 Postgrespostgresql://postgres:passwordpostgres:5432/electric?sslmodedisable设置ELECTRIC_INSECURE: true配置文件注释明确警告不适合生产环境仅应在开发时或已通过其他方式保护 Electric API 时使用端口映射3000:3000即 Electric 的 Shape HTTP 端点默认监听地址build字段指向../packages/sync-service/允许在需要时从源码构建镜像而非拉取远程镜像。数据库结构与迁移backend:up的第二步是执行db:migrate: dotenv -e ../../.env.dev -- pnpm exec pg-migrations apply --directory ./db/migrations该命令从仓库根 .env.dev 读取DATABASE_URL等环境变量用databases/pg-migrations把 db/migrations 目录下的 SQL 按序应用到数据库。示例仅有一个迁移文件 db/migrations/001-create-todos.sqlCREATE TABLE IF NOT EXISTS todos ( id UUID PRIMARY KEY, title TEXT NOT NULL, completed BOOLEAN NOT NULL, created_at TIMESTAMP WITH TIME ZONE NOT NULL );表结构与前端 TypeScript 类型一一对应见 src/routes/index.tsx 中的ToDo类型id为 UUID 主键客户端用uuid库生成title为待办文本completed为完成状态布尔值created_at为带时区时间戳用于前端排序。注意迁移文件位于db/migrations目录下这正是 Electric 官方示例与文档推荐的迁移管理方式数据库结构变更与同步配置一起版本化。启动开发服务器pnpm dev后端就绪后在示例目录启动开发服务器pnpm dev对应脚本为dev: dotenv -e .env -- concurrently \vite\ \node server.js\它从示例目录的 .env仓库中未提交由开发环境提供读取配置并用concurrently同时启动两个进程Vite 开发服务器基于 vite.config.ts仅启用vitejs/plugin-react-swc托管 React 前端。应用入口 src/main.tsx 通过 React Router 的createBrowserRouter注册/路由根路由 src/routes/root.tsx 仅渲染Outlet /业务页面对应 src/routes/index.tsx整体包裹在 Radix UI 的暗色Theme中。Node.js 写代理服务server.js 是一个 Express 应用监听3010端口提供/todos的 REST 接口。前端通过环境变量VITE_SERVER_URL定位后端地址在 src/routes/index.tsx 中形如new URL(\${import.meta.env.VITE_SERVER_URL}/todos)所有数据读写都经由该服务。应用实现剖析读订阅与写代理读路径useShape 响应式订阅前端核心是 src/routes/index.tsx 中的useShapeconst { data: todos } useShapeToDo({ url: new URL(${import.meta.env.VITE_SERVER_URL}/todos).href, }) todos.sort((a, b) a.created_at - b.created_at)useShape来自electric-sql/react对应 packages/react-hooks 与 packages/typescript-client它订阅一个 Shape URL将结果映射为todos数组。当 Postgres 中todos表发生任何写入/更新/删除时Electric 会通过逻辑复制捕获变更并推送React 组件自动重渲染——因此列表无需手动刷新即可实时反映所有客户端包括其他浏览器窗口的修改。示例在渲染前按created_at升序排序保证新增待办追加到列表末尾。空列表时显示 No to-dos to show - add one! 占位提示。写路径Express pg 直连数据库与读路径走 Shape 同步不同写路径统一通过 server.js 的 REST 接口完成POST/todos前端生成iduuidv4()与title后提交服务端先用 zod 的postSchema校验id必须为 UUID、title为字符串再执行参数化 INSERTcompleted默认false、created_at取当前时间PUT/todos/:id点击待办卡片时触发通过putSchema校验可选字段并借助generateUpdateQuery动态拼接UPDATE todos SET ... WHERE id ...的参数化语句切换completed状态DELETE/todos/:id点击卡片右侧的 X 幽灵按钮触发e.stopPropagation()防止冒泡到卡片的点击切换逻辑执行DELETE from todos where id $1。服务端所有 SQL 均使用$1参数化占位符避免 SQL 注入数据库写入成功后Postgres 的 WAL 变更会被 Electric 捕获并广播所有useShape订阅端随之更新形成写库 → 同步 → 多端一致的闭环。GET /todosShape 代理与鉴权透传server.js 中最具架构参考价值的是GET /todos它并不查数据库而是将请求代理到 Electric 的 Shape 端点const electricUrl new URL(${ELECTRIC_URL}/v1/shape) // 仅透传 Electric 协议参数 Object.keys(req.query).forEach((key) { if (ELECTRIC_PROTOCOL_QUERY_PARAMS.includes(key)) { electricUrl.searchParams.set(key, req.query[key]) } }) electricUrl.searchParams.set(table, todos)关键设计有三点ELECTRIC_PROTOCOL_QUERY_PARAMS从electric-sql/client导入是客户端握手所需的协议参数白名单代理只透传白名单内的参数其余查询参数一律丢弃避免任意参数被转发表名todos由服务端强制指定客户端无法自定义要同步的表若配置了ELECTRIC_SOURCE_ID/ELECTRIC_SOURCE_SECRET会附加source_id与secret查询参数完成对 Electric 的源鉴权。随后代理把 Electric 返回的 Web Stream 转为 Node 流管道回客户端并剥离content-encoding、content-length两个可能破坏解码的响应头对ERR_STREAM_PREMATURE_CLOSE客户端提前断开等错误做了静默容错。该模式让示例可以复用 Electric 的原生同步协议同时把鉴权、参数校验等逻辑收敛在自家后端。停止后端服务开发完成后在示例目录执行pnpm backend:down对应脚本为backend:down: PROJECT_NAMEtodo-app-example pnpm -C ../../ run example-backend:down最终调用根目录的example-backend:downdocker compose ... down --volumes停止 postgres 与 backend 两个容器并删除卷。由于数据目录本身是 tmpfs即使不手动清理重启后数据也会自动归零。部署形态参考除本地开发外示例还通过 sst.config.ts 提供云端部署编排可作为理解生产形态的参考使用 Neon 提供托管 Postgres 并自动应用./db/migrations迁移createDatabaseForCloudElectric同时产出sourceId/sourceSecret用于 Electric 鉴权在共享 ECS 集群上部署 Node 后端容器通过负载均衡把443/https转发到容器内3010/http健康检查指向/health前端以sst.aws.StaticSite构建部署并把后端地址注入为VITE_SERVER_URL环境变量。整个编排同样体现了后端持有DATABASE_URL与ELECTRIC_URL、前端只拿公开 URL的隔离原则。小结通过本文的步骤你可以完整走通 ElectricSQL 官方 TodoMVC 示例在 examples/todo-app 中先pnpm install pnpm run -r build构建 monorepo再用pnpm backend:up一键拉起并自动清理Postgres 与 Electric 后端、应用迁移随后pnpm dev并行启动 Vite 前端与 Express 写代理。源码层面src/routes/index.tsx 演示了useShape的声明式订阅server.js 演示了参数化写入、Shape 代理与协议参数白名单过滤而 .support/docker-compose.yml 则展示了为 Electric 调优过的本地开发环境模板。这套读走 Shape、写走 API的架构正是将 ElectricSQL 集成进既有服务端工程时的推荐起点。【免费下载链接】electricThe agent platform built on sync.项目地址: https://gitcode.com/GitHub_Trending/el/electric创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考