Fly.io上Next.js预渲染路由构建时PostgreSQL连接失败求助
解决Fly.io部署Next.js App目录API路由预渲染时的PostgreSQL连接问题
核心原因
Fly.io的构建阶段无法直接访问Fly托管的Postgres实例,且默认不会加载应用的secrets(secrets仅在运行阶段生效)。Next.js App Router的API路由如果是静态预渲染(默认GET请求会尝试静态生成),构建时会执行路由内的数据库查询,导致连接失败。
简便解决方案
方案1:强制API路由动态运行(最快捷)
在你的/api/my-api-route/route.js(或.ts)文件顶部添加以下代码,让Next.js在构建时跳过预渲染,部署后运行时再执行数据库查询:
export const dynamic = 'force-dynamic';
这样构建阶段不会执行数据库操作,部署后应用会使用Fly secrets中配置的DATABASE_URL连接Postgres,无需修改构建流程。
方案2:安全传递构建阶段所需的DATABASE_URL
如果需要保留静态预渲染,可通过Fly的构建秘密(Build Secrets)安全传递DATABASE_URL到构建环境:
- 执行部署命令时传递构建秘密:
fly deploy --build-secret DATABASE_URL=$(fly secrets get DATABASE_URL)
- 修改生成的Dockerfile,在
npm run build步骤挂载构建秘密:
# 替换原有的npm run build步骤 RUN --mount=type=secret,id=DATABASE_URL,env=DATABASE_URL npm run build
此方法不会将DATABASE_URL明文写入镜像或暴露在构建日志中,同时构建阶段能正常连接Postgres完成预渲染。
方案3:构建阶段使用Mock数据
如果不需要静态预渲染的真实数据,可在代码中判断构建环境,返回mock数据:
import { NextResponse } from 'next/server'; import { Pool } from 'pg'; export async function GET() { // 判断是否为Fly构建阶段 const isBuildStage = process.env.NODE_ENV === 'production' && process.env.FLY_BUILD_STAGE === 'true'; if (isBuildStage) { // 返回mock数据跳过真实查询 return NextResponse.json([{ id: 1, name: 'Mock Data' }]); } // 运行阶段执行真实查询 const pool = new Pool({ connectionString: process.env.DATABASE_URL }); const res = await pool.query('SELECT * FROM test'); await pool.end(); return NextResponse.json(res.rows); }
部署时传递构建秘密设置环境变量:
fly deploy --build-secret FLY_BUILD_STAGE=true
为什么之前的方法无效
ENV DATABASE_URL="postgres://:memory:":pg包不支持内存PostgreSQL,无法正常执行查询。- Dockerfile写入实际
DATABASE_URL:会将敏感信息明文打包进镜像,存在安全风险,且URL变更后需重新修改Dockerfile。 --build-arg传递:参数会暴露在构建日志中,不安全,且Fly构建环境默认限制了对内部Postgres实例的访问(即使拿到URL,构建阶段也无法连接)。
内容的提问来源于stack exchange,提问作者Ilya
相关产品推荐
相关产品推荐

