RedwoodJS 全栈应用部署实战:从 SQLite 到 Postgres 再到 Netlify 的完整上线指南
【免费下载链接】redwoodRedwoodGraphQL项目地址: https://gitcode.com/gh_mirrors/re/redwood
本篇指南以 RedwoodJS(Redwood 框架)教程第五章部署章节为核心,完整讲解如何将一个本地基于 SQLite 的 Redwood 全栈应用迁移至 Postgres 数据库,并借助 Netlify 的 Git 集成完成生产环境部署。你将掌握数据库提供方切换、迁移重建、环境变量配置、Netlify 部署设置、自定义域名与分支预览,以及 Serverless 场景下数据库连接与安全加固的完整实战方案。
为什么部署是 RedwoodJS 的"最后一公里"
RedwoodJS 设计之初就以"让全栈 Web 应用在 Jamstack 上更容易构建和部署"为目标。教程前几章虽然已经让应用跑了起来,但那只是本地开发环境;真正让它面向互联网"活"起来,需要解决两个核心问题:把数据放到互联网上可达的数据库,以及把代码托管给持续集成部署平台。
本教程演示的路径是业界最典型的 Redwood 部署组合:
- Git托管代码(GitHub / GitLab / Bitbucket)
- Railway(或其他托管服务)提供 Postgres 实例
- Netlify负责构建与托管 Web 端和 Serverless 函数
先把代码推上 Git
如果你此前一直跳过 Git 使用教程,那么从部署这一步开始必须启用它。最简单的方式是在 GitHub(或 GitLab / Bitbucket)新建一个仓库,平台会给出把本地代码提交并推送所需的完整命令清单。与新建空仓库时"只添加 README"不同,这里要提交整个代码库:
git add . git commit -m "Initial commit" git push数据库:为什么本地 SQLite 不能直接上生产
RedwoodJS 本地开发默认使用 SQLite,其文件型数据库依赖持久化磁盘存储。而 Serverless 部署环境(如 Netlify 背后的 AWS Lambda)没有可写入的文件系统持久层,因此必须切换到 Postgres。Prisma 目前支持 SQLite、Postgres、MySQL 和 SQL Server,但同一时间只能使用一种数据库 provider——这意味着一旦切换,本地开发也必须改用同一数据库,否则会出现本地与生产环境行为不一致的问题。
:::danger 关键约束
Prisma 同一时间仅支持一个数据库 provider。既然生产环境无法使用 SQLite、必须改用 Postgres 或 MySQL,那么本地开发环境在切换后也必须使用相同数据库。请参阅 本地 Postgres 环境搭建指南 完成本地环境准备。
:::
从 本地 Postgres 环境搭建指南 可以看到完整的本地切换路径:通过brew install postgresql@14(Mac)等方式安装 Postgres,用createdb创建数据库,然后在.env中配置DATABASE_URL:
DATABASE_URL="postgresql://postgres@localhost:5432/redwoodblog_dev?connection_limit=1"注意其中的connection_limit=1参数——这是 Prisma 官方在 Serverless 场景下针对关系型数据库的推荐配置,生产环境的DATABASE_URL同样需要追加该参数(后文 Netlify 环境变量配置中会再次用到)。
选择一个托管 Postgres 服务
教程给出了多个可快速创建 Postgres 实例的托管服务:
- Railway(免费、无需登录即可起步,但不注册账户数据库会在 24 小时后被删除)
- Heroku Postgres
- Digital Ocean Managed Databases
- AWS RDS for PostgreSQL
教程选择 Railway,因为其起步门槛最低。操作流程为:进入 Railway 点击Start a New Project,然后选择 Provision PostgreSQL。创建完成后,在左侧点击PostgreSQL,进入Connect选项卡,复制以postgresql://开头的Postgres Connection URL,该连接串就是后续配置的核心凭据。
切换数据库 Provider 并重建迁移
拿到生产数据库连接串后,需要完成三步切换:
第一步:修改 Prisma schema
更新api/db/schema.prisma中的provider配置(原文档示例为schema.prisma,若项目按 Redwood 标准布局则为api/db/schema.prisma,可参考 本地 Postgres 环境搭建指南 中的datasource db块):
datasource db { provider = "postgresql" url = env("DATABASE_URL") }第二步:配置 DATABASE_URL
打开项目根目录的.env文件,取消DATABASE_URL变量的注释,并填入从 Railway 复制的连接串。
:::info.env与.env.defaults的分工
.env默认不会(也绝不应该)被提交进 Git。它存放代码库所需的全部机密信息——数据库 URL、API 密钥等,一旦提交到公开仓库,任何人都能看到你的秘密数据。而.env.defaults用于存放非敏感的公共环境变量(如日志级别、第三方库的非敏感配置),它应当提交进仓库与团队共享。
:::
第三步:删除旧迁移并重新生成
SQLite 格式的迁移文件无法在 Postgres 上直接使用,需要完全删除后重新生成:
rm -rf api/db/migrations # 删除旧迁移目录(Windows 下用 rmdir /s api\db\migrations) yarn rw prisma migrate devyarn rw prisma migrate dev会把此前所有 schema 变更合并为一个全新的迁移文件,并直接应用到 Railway 上的远程数据库实例。命令执行时会要求为迁移命名,可以起一个类似 "initial schema" 的名字。
如果此步骤遇到 "PrismaClientInitializationError" 之类的错误,可先执行yarn rw prisma generate重新生成 Prisma Client(参见 本地 Postgres 环境搭建指南 中的提示)。注意:本地 Postgres 服务需要手动启停,Redwood CLI 不会像 SQLite 那样自动管理它。
接入 Netlify:构建与部署配置
数据库就绪后,接下来把代码部署到 Netlify。标准做法分为"命令行生成配置"与"网页端关联仓库"两步。
生成 netlify.toml
在项目根目录执行:
yarn rw setup deploy netlify从源码看,该命令由 packages/cli/src/commands/setup/deploy/providers/netlify.js 实现,它实际做了两件事:把api侧的 API URL 更新为/.netlify/functions(对应 Netlify 的函数挂载路径),并向项目根目录写入netlify.toml。该配置文件由 packages/cli/src/commands/setup/deploy/templates/netlify.js 中的NETLIFY_TOML模板生成,内容如下:
[build] command = "yarn rw deploy netlify" publish = "web/dist" functions = "api/dist/functions" [build.environment] NODE_VERSION = "20" [[redirects]] from = "/*" to = "/200.html" status = 200 [dev] framework = "redwoodjs" # 确保 targetPort 与 redwood.toml 中的 web.port 一致 targetPort = 8910 # 浏览器访问端口 port = 8888各配置项含义:
[build]:Netlify 的构建配置段。command指定构建命令;publish是静态资源输出目录(Web 端构建产物);functions是 Serverless 函数目录(API 端产物)。[build.environment]:构建期环境变量,模板默认指定NODE_VERSION = "20",确保构建环境与 RedwoodJS 要求的 Node 版本一致。[[redirects]]:将根路径/*的所有请求重定向到200.html。这是 Redwood 的 SPA 路由回退(fallback)机制,让浏览器端路由在静态托管环境下正常工作。[dev]:Netlify Dev 本地调试配置,framework = "redwoodjs"让 Netlify 识别 Redwood 项目。
其中构建命令yarn rw deploy netlify由 packages/cli/src/commands/deploy/netlify.js 提供,它复用 packages/cli/src/commands/deploy/helpers/helpers.js 中的通用逻辑,按顺序执行三条命令:
yarn rw build --verbose # 生产构建(--build,默认开启) yarn rw prisma migrate deploy # 应用数据库迁移(--prisma,默认开启) yarn rw>yarn rw g secret从源码 packages/cli/src/commands/generate/secret/secret.js 可以看到它的实现:使用 Node 的crypto.randomBytes生成 32 字节随机数并做 base64 编码(默认长度 32,可通过--length调整;--raw可只输出裸字符串)。命令输出后还会提示:如果配合 dbAuth 使用,请将其设为SESSION_SECRET环境变量。
为什么这个变量不可或缺?在 packages/auth-providers/dbAuth/api/src/DbAuthHandler.ts 的_validateOptions()中,dbAuth 在登录/注册前会强制校验SESSION_SECRET:
// validates that we have all the ENV and options we need to login/signup _validateOptions() { // must have a SESSION_SECRET so we can encrypt/decrypt the cookie if (!process.env.SESSION_SECRET) { throw new DbAuthError.NoSessionSecretError() } ... }没有SESSION_SECRET,dbAuth 会直接抛出NoSessionSecretError。把DATABASE_URL和SESSION_SECRET两个变量都配置好后,点击Create variable。
触发部署并验证
进入顶部导航的Deploys选项卡,打开右侧Trigger deploy下拉菜单,选择Deploy site,Netlify 将重新拉取仓库并执行完整构建流程(构建 → 迁移 → 部署函数)。
部署成功后,点击部署日志页顶部的Preview按钮,或返回站点首页点击 Netlify 站点 URL 即可访问线上应用。
:::info Preview 与正式 URL 的区别
通过Preview按钮查看的部署,URL 中包含最近一次提交的 hash。Netlify 会为每次推送到main的提交都生成一个这样的预览地址,但它只会展示该次确切提交的内容——如果你再次部署后刷新这个预览页,看不到任何变化。而站点主页上展示的真实 URL 始终指向最近一次成功部署的结果。关于分支部署的更多细节见下文 Branch Deploys。
:::
如何判断是否成功?如果首页 About 和 Contact 链接下显示"Empty",就说明部署成功了——因为全新的生产数据库里还没有任何文章。此时访问/admin/posts创建几篇文章,再回到首页就能看到它们。
如果部署失败:
- 检查 Netlify 构建日志中的报错输出;
- 如果部署成功但站点无法访问,打开浏览器开发者工具(Web Inspector)查看前端报错;
- 确认是否完整粘贴了 Postgres 连接串;
- 仍无法解决时,可到 Redwood 社区论坛求助。
自定义子域名与自定义域名
默认的agitated-mongoose-849e99.netlify.app这类随机域名显然不理想。在Site Settings>Domain Management>Domains>Custom Domains中,打开Options菜单选择Edit site name,即可把站点发布到自定义子域名(如redwood-tutorial.netlify.app),几乎立即生效。
注意两点:
- 子域名在 Netlify 全局范围内必须唯一,
blog.netlify.app这类常见名称大概率已被占用; - 如需完全自定义域名,点击Add custom domain按钮,按提示绑定你拥有的域名。
分支部署(Branch Deploys)与部署预览
Netlify 的**分支部署(Branch Deploys)**是很有用的特性:当你创建分支并推送到仓库时,Netlify 会为这个分支在独立 URL 上构建一次部署,方便在不动主站的情况下测试改动。分支合并回main后,主站自动触发部署,改动即对全世界可见。
开启方式:Site settings>Build & deploy>Continuous Deployment,在Branches区域点击Edit settings,把Branch deploys改为 "All"。同时可以启用Deploy previews,为针对该仓库的任何 Pull Request 自动生成预览部署。
:::tip 锁定 main 分支
你还可以"锁定"main分支,让主站不在每次 push 时自动部署,而是需要手动触发(通过站点控制台或 Netlify CLI)才部署最新版本。
:::
数据库运维关注点
连接数:Serverless 场景的连接池问题
教程部署方案中,每个 Serverless 函数都会直连Postgres 数据库。而 Postgres 能接受的并发连接数有限(默认 100;MySQL 默认 151)。设想流量洪峰使 Serverless 函数调用量增长 100 倍:Netlify(底层是 AWS)会毫不犹豫地拉起 100+ 个 Lambda 实例来处理流量,问题在于每个实例都会各自打开一条数据库连接,很可能瞬间耗尽可用连接数。
正确的做法是在 Postgres 前面架设连接池服务,让 Lambda 函数连接连接池而非直连数据库。详细方案参见 Connection Pooling 指南,其中介绍了多种选择:
- Prisma Data Proxy:Prisma 官方的连接管理与池化服务,支持 MySQL 和 Postgres;
- Prisma + PgBouncer:在 Prisma Client 与数据库之间放置 PgBouncer 作为连接池代理,Serverless 函数中需要在连接串上追加
?pgbouncer=true(PgBouncer 端口通常是 6543,区别于 Postgres 默认的 5432)。注意:Prisma Migrate 依赖事务检查迁移状态,不能走 PgBouncer 执行迁移,迁移时必须直连数据库; - Supabase / Heroku / Digital Ocean / AWS RDS Proxy等托管方案各有对应配置(如 AWS 的 RDS Proxy 必须与 Lambda 在同一 VPC 内、不可公开访问)。
安全性:开放数据库的权衡
生产数据库必须对公网开放,因为 Serverless 函数运行时的 IP 地址不可预知。理论上可以获取托管商全部 IP 的 CIDR 网段并做白名单,但这些网段通常随时间变化,维护成本很高。只要数据库用户名/密码保管得当,这种"开放但加密凭据"的方案在可接受范围内,尽管并非理想方案。
堵住注册漏洞:改造 dbAuth 的 signupOptions
安全方面还有一个明显的隐患:任何人都能注册新账户并开始创建博客文章。一个快速且有效的缓解措施是:创建完自己的账户后,移除signup路由,让普通用户无法访问注册页面。但这对"老练的黑客"无效——dbAuth 的注册和登录 API 是客户端可调用的,即使没有注册页面,攻击者依然可以直接构造 API 请求调用同一端点创建新用户。
堵住这个洞的关键在api/src/functions/auth.js(TypeScript 项目为auth.ts),这是 dbAuth 配置所在。其中signupOptions对象里的handler()函数定义了收到注册表单数据后的行为——只需让它返回false而不创建用户,就从 API 层面关上了注册的大门。
从 dbAuth 的模板源码 packages/auth-providers/dbAuth/setup/src/templates/api/functions/auth.ts.template 可以看到默认实现的完整结构:
const signupOptions: DbAuthHandlerOptions<UserType, UserAttributes>['signup'] = { // 新用户注册时对数据的处理。Redwood 在调用此 handler 前会先检查用户名是否重复。 // 至少需要把 username、hashedPassword 和 salt 保存到 user 表。 // userAttributes 包含传给 signUp() 的对象中任何额外的成员。 handler: ({ username, hashedPassword, salt, userAttributes: _userAttributes }) => { return db.user.create({ data: { email: username, hashedPassword: hashedPassword, salt: salt, // name: userAttributes.name }, }) }, // 密码格式校验。返回 true 表示密码合法,否则抛出 PasswordValidationError。 passwordValidation: (_password) => { return true }, errors: { fieldMissing: '${field} is required', usernameTaken: 'Username `${username}` already in use', }, }默认的handler()会执行db.user.create(...)创建用户。要关闭公开注册,把handler改为:
handler: () => { return false },如此,即使用户数据被提交,dbAuth 也不会创建任何账户。修改完成后提交并推送代码,Netlify 会自动重新部署,注册漏洞即被堵住。
总结
至此,一个 RedwoodJS 全栈应用完成了从本地 SQLite 到生产环境的完整上线:Git 托管代码 → 托管 Postgres 承载数据 → 重建 Prisma 迁移 → Netlify 关联仓库并自动构建 → 配置DATABASE_URL与SESSION_SECRET→ 触发部署并验证 → 自定义域名与分支预览 → 理解连接池与数据库安全 → 通过改造 dbAuth 堵住公开注册漏洞。
这套流程的核心经验可以沉淀为三点:生产与开发必须使用同一数据库 provider;让 CI/CD 平台(而非本地 CLI)执行构建以保证 Prisma 引擎与运行环境匹配;Serverless 架构下数据库连接与安全需要提前规划(连接池、connection_limit=1、独立的SESSION_SECRET)。掌握这些,你就能把任何 RedwoodJS 应用可靠地部署到 Jamstack 世界。
【免费下载链接】redwoodRedwoodGraphQL项目地址: https://gitcode.com/gh_mirrors/re/redwood
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考