部署Node.js Azure Functions v4时引入@azure/cosmos导致函数处理器无法被识别的问题
我之前也遇到过类似的依赖打包导致Azure Functions无法识别函数的问题,结合你的描述,这大概率是tsup的打包配置或者@azure/cosmos的依赖处理和Azure Functions运行时不兼容导致的,下面一步步来分析和解决:
问题回顾
你在部署Node.js Azure Functions v4应用时,只要引入并使用@azure/cosmos包,Azure运行时就无法检测到函数处理器;部署看似成功,但门户里函数列表为空,所有API请求返回404。注释掉@azure/cosmos的导入和使用后,一切正常。
环境信息
- Azure Functions Runtime: v4
- Node.js Version: v22.x
- TypeScript Version: latest
- Bundler: tsup v8.5.0
- 核心依赖版本:
@azure/functions: v4.7.3、@azure/cosmos: v4.4.1 - 部署命令:
func azure functionapp publish <app-name>
问题根源分析
核心问题出在tsup的打包配置和Azure Functions运行时的依赖加载逻辑不匹配:
你的tsup配置里把@azure/cosmos标记为了external(外部依赖),这意味着打包后的代码不会把@azure/cosmos的代码包含进去,而是保留导入语句,让运行时去node_modules里找这个包。但实际部署时会出现两个关键问题:
- Azure Functions部署过程中可能没有正确上传
@azure/cosmos依赖到函数应用目录 - Node.js v22的ESM模块解析逻辑和
@azure/cosmos的内部依赖结构存在兼容问题,导致运行时加载@azure/cosmos失败,进而整个函数入口文件执行中断,Azure Functions根本没机会完成函数注册
另外,你在代码里提前在函数定义之外实例化了CosmosClient,如果这个实例化过程因为依赖加载失败抛出异常,会直接终止入口文件的执行,彻底阻断函数的注册流程。
解决方案
方案1:调整tsup打包配置,将@azure/cosmos打包进产物
修改你的tsup.config.ts,把@azure/cosmos从external数组中移除,让tsup把它的代码直接打包到dist目录里,避免运行时依赖加载失败:
import { defineConfig } from 'tsup' import alias from 'esbuild-plugin-alias' import path from 'path' export default defineConfig({ entry: ['src/index.ts'], outDir: 'dist', format: 'esm', target: 'node22', splitting: false, sourcemap: false, dts: false, // 仅保留@azure/functions为外部依赖(Azure运行时会自动处理它的加载) external: ['@azure/functions'], clean: true, esbuildPlugins: [ alias({ '#common': path.resolve(__dirname, '../common/src'), }), ], })
方案2:延迟CosmosClient的实例化(避免入口初始化失败)
即使调整了打包配置,也建议你不要在函数定义之外提前实例化CosmosClient,而是把实例化逻辑放在函数处理内部,这样即使依赖加载出问题,也只会在调用函数时报错,不会导致函数无法被Azure识别:
import { app } from "@azure/functions"; import { CosmosClient } from "@azure/cosmos"; // 仅定义配置,不提前实例化客户端 const COSMOS_DB_ENDPOINT = process.env.COSMOS_DB_ENDPOINT; const COSMOS_DB_KEY = process.env.COSMOS_DB_KEY; const COSMOS_DB_NAME = process.env.COSMOS_DB_DATABASE_ID; const cosmosConfig = { endpoint: COSMOS_DB_ENDPOINT, key: COSMOS_DB_KEY, databaseId: COSMOS_DB_NAME, containers: { users: "Users" }, }; app.http("get-user-details", { methods: ["GET"], authLevel: "anonymous", route: "user/{userId}", handler: async (request) => { try { // 在函数执行时才实例化CosmosClient const cosmosClient = new CosmosClient({ endpoint: cosmosConfig.endpoint, key: cosmosConfig.key, connectionPolicy: { enableEndpointDiscovery: true } }); const container = cosmosClient.database(cosmosConfig.databaseId).container("Items"); // 后续业务逻辑 console.log(container); return { status: 200, jsonBody: { data: "validatedUserItem" } }; } catch (error) { console.log("error: ", error); return { status: 500, jsonBody: { data: "error" } }; } } });
方案3:确保部署时正确上传node_modules(适合坚持外部依赖的场景)
如果你还是想把@azure/cosmos作为外部依赖,需要确保部署时把node_modules一起上传到Azure:
- 本地先执行
npm install确保node_modules包含@azure/cosmos - 使用
func azure functionapp publish <app-name> --no-build命令部署,避免Azure在部署时重新构建导致依赖丢失 - 或者在Azure Portal的Function App配置中,设置
SCM_DO_BUILD_DURING_DEPLOYMENT为true,让Azure在部署时自动安装依赖
验证步骤
- 按照上述任意方案调整配置或代码
- 重新执行
npm run build完成打包 - 用
func azure functionapp publish <app-name>部署应用 - 登录Azure Portal查看Function App的「Functions」标签,确认
get-user-details触发器已被识别 - 调用
.../api/user/{userId}端点,验证是否返回正常响应
内容来源于stack exchange

