为什么 Deno 脚本在 dash 正常运行,迁移到 console 后部署失败?
仅设置 Entrypoint: main-esm.ts 还不足以完成迁移。应优先检查旧 HTTP API、数据库连接初始化和新平台的环境变量配置。目前没有失败日志,无法确定哪一项直接导致了此次部署失败。
先确认失败发生在哪个阶段
Deno Deploy 的部署流程包括依赖安装、构建和应用启动预热等阶段。任何一步失败,最终都可能显示为 Failed,因此部署失败不一定意味着 TypeScript 编译失败。官方构建流程说明
打开失败记录,查看具体阶段和第一条相关错误:
- 找不到文件:检查 GitHub 分支、应用目录和入口路径。
- 依赖下载失败:检查报错对应的 import URL 和依赖版本。
- TypeScript 错误:根据报错修正 API 或参数类型。
- 启动异常、连接拒绝或预热超时:检查 HTTP 服务是否启动,以及启动前的数据库连接是否完成。
对于这个单文件服务,应选择动态运行模式,并确认入口路径相对于配置的应用目录正确。如果没有额外的编译过程,可以不设置 Build command。不要把持续运行的 deno run main-esm.ts 当作构建命令:入口负责启动服务,构建命令应执行后退出。应用配置说明
将 HTTP 服务改为 Deno.serve()
代码使用了 Deno.listen() 和 Deno.serveHttp()。Deno 2 迁移指南将 Deno.serveHttp() 列为软移除 API:它的类型定义已移除,但指南所述的实现仍为兼容而保留。如果构建时执行类型检查,这里可能报错;不能仅凭 API 名称断定它在运行时一定不存在。建议改用 Deno.serve()。Deno 2 迁移指南
先暂时移除数据库连接和其他 import,只部署以下 main-esm.ts,检查入口配置和基本 HTTP 服务能否工作:
Deno.serve({ port: 8080 }, () => {
return new Response("OK", { status: 200 });
});
这一步用于隔离问题,不能保证部署成功。如果仍然失败,继续检查入口和部署日志;如果成功,再逐步恢复环境变量、MySQL 依赖和业务逻辑。官方平台迁移指南也建议使用内置的 Deno.serve()。Deno Deploy 迁移指南
检查环境变量和数据库初始化
原代码在启动 HTTP 服务前连接数据库:
const client = await new Client().connect({
// ...
});
数据库连接抛错会使程序无法执行到启动监听的位置;如果连接迟迟未完成,也会阻塞服务启动。这是根据代码执行顺序得出的排查方向,是否确实发生还需要日志确认。
在新应用中重新检查以下变量,确认它们属于实际部署使用的环境:
DATABASE_HOST
DATABASE_USER
DATABASE_NAME
DATABASE_PASS
DATABASE_PORT
新平台区分 Production、Development 和 Build 等环境变量上下文,需要重新核对配置。不要假定旧平台的变量会随 GitHub 代码一同迁移。环境变量迁移说明
Deno.env.get() 返回 string | undefined。数据库端口应先转换为数字并验证,再传给驱动:
const rawPort = Deno.env.get("DATABASE_PORT");
if (!rawPort) {
throw new Error("Missing DATABASE_PORT");
}
const port = Number(rawPort);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
throw new Error("Invalid DATABASE_PORT");
}
随后在连接配置中使用 port。其他必需变量也应检查是否缺失,日志中不要输出密码。
如果生产环境直接通过平台注入变量,可以移除用于加载本地 .env 的 dotenv/load.ts。如果继续保留,应确认它的文件读取行为符合部署环境。数据库连接失败时,再根据错误检查地址、端口、访问控制、凭据和 TLS 要求。
MySQL import 没有固定版本。排查迁移问题时,应确认实际解析的版本及其 Deno 兼容性,再固定经过验证的版本。现有材料不足以认定驱动是故障来源。
修复请求处理中的问题
即使部署成功,原代码的 / 分支仍有几个地方需要调整:
save2MySQL()是异步函数,调用时没有await,记录到的会是 Promise。/分支没有调用respondWith(),因此没有为该请求返回正常响应。searchParams.get("sentence")可能返回null,应先验证,再传给需要string的函数。counter as unknown as string只是类型断言;需要字符串时应使用String(counter)。- 示例中的
result没有展示定义。如果它存在于省略的代码中,则无须处理;如果实际文件中也没有定义,就需要修复。
迁移后的 HTTP 部分可以参考以下写法。这里假设保留了实际的数据库初始化,以及能够执行写入并返回结果的 save2MySQL() 实现:
let counter = 0;
Deno.serve({ port: 8080 }, async (request) => {
counter++;
const url = new URL(request.url);
if (url.pathname !== "/") {
return new Response(String(counter), { status: 200 });
}
const sentence = url.searchParams.get("sentence");
if (sentence === null || sentence.trim() === "") {
return new Response("Missing sentence", { status: 400 });
}
try {
await save2MySQL(sentence);
return new Response("Saved", { status: 200 });
} catch (error) {
console.error("Database write failed", error);
return new Response("Database write failed", { status: 500 });
}
});
这个片段用于替换原来的监听和请求循环,不应与旧循环同时运行。原问题省略了 SQL 和 result 的来源,因此这里没有补写数据库写入实现。
重新部署前如何验证
可以先用与部署环境一致的 Deno 版本,在本地执行类型检查:
deno check main-esm.ts
类型检查通过只能说明相关类型问题已解决,不能证明远程依赖、平台配置或数据库网络一定正常。最终仍需查看部署日志,并实际请求服务。
当前最有效的处理顺序是先确认失败阶段,部署最小 Deno.serve() 服务,再恢复数据库连接和业务逻辑。确定故障原因仍需要具体错误信息。
备注:内容仅供参考。
