PAYATHON 2026

为什么 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() 服务,再恢复数据库连接和业务逻辑。确定故障原因仍需要具体错误信息。

备注:内容仅供参考。