使用SAM部署Lambda时,如何导入Layer中的node_modules并解决Esbuild报错?
我想用SAM本地测试并部署Lambda函数及Layer,目标是在函数代码里导入Layer目录下的node_modules模块,而非函数自身目录内的node_modules。但执行命令cd functions-stack && sam build --beta-feature时,出现了Esbuild Failed: Could not resolve <module name>的错误。
当前目录结构:
functions-stack/ template.yml my-function/ .aws-sam/ build/ <- 空目录 app.ts node_modules/ layers-stack/ template.yml nodejs/ node_modules/ package.json
函数模板配置(functions-stack/my-function/template.yml):
Resources: MyFunction: Type: AWS::Serverless::Function Properties: FunctionName: my-function CodeUri: ./my-function/ Handler: app.lambdaHandler Runtime: nodejs14.x Architectures: - x86_64 Layers: - arn:aws:lambda:ap-northeast-1:123456789:layer:NodejsLayer:1 Metadata: BuildMethod: esbuild BuildProperties: Minify: true Target: "es2020" Sourcemap: true EntryPoints: - app.ts
函数代码(functions-stack/my-function/app.ts):
import { APIGatewayProxyEvent } from 'aws-lambda' // 这个模块安装在layers-stack/node_modules里 // 报错:Esbuild Failed: ✘ [ERROR] Could not resolve "axios" import axios from 'axios' export const lambdaHandler = async (event: APIGatewayProxyEvent) => { return }
核心原因
SAM构建时,esbuild只会默认查找函数目录下的node_modules,不会自动识别远程Layer或本地Layer的依赖路径,必须手动配置让esbuild定位到Layer里的模块。
1. 本地构建时关联Layer的本地路径
方法一:修改模板引用本地Layer(适合长期本地开发)
在functions-stack/template.yml中新增本地Layer的资源定义,让函数直接引用这个本地Layer:
Resources: # 新增本地Layer资源 NodejsLayer: Type: AWS::Serverless::LayerVersion Properties: LayerName: NodejsLayer ContentUri: ../layers-stack/nodejs/ # 指向本地Layer的nodejs目录 CompatibleRuntimes: - nodejs14.x # 原函数配置 MyFunction: Type: AWS::Serverless::Function Properties: # 其他配置不变 Layers: - !Ref NodejsLayer # 替换原来的远程ARN,引用本地Layer # 原Metadata配置不变
方法二:构建时临时指定Layer路径(适合临时测试)
无需修改模板,直接在构建命令中加入--layer-source参数关联本地Layer:
cd functions-stack && sam build --beta-feature --layer-source NodejsLayer=../layers-stack/nodejs/
2. 配置Esbuild忽略Layer依赖并指定解析路径
在函数的Metadata.BuildProperties中添加两项配置:
External:告诉esbuild不要打包Layer中的模块(运行时由Layer提供)Paths:指定Layer中node_modules的路径,让esbuild能找到对应模块
修改后的模板配置:
Metadata: BuildMethod: esbuild BuildProperties: Minify: true Target: "es2020" Sourcemap: true EntryPoints: - app.ts External: - axios # 列出所有Layer中的依赖,多个模块直接添加到数组即可 Paths: "*": ../layers-stack/nodejs/node_modules/ # 批量指定所有模块的解析路径
如果只有个别模块,也可以单独指定:
Paths: axios: ../layers-stack/nodejs/node_modules/axios
3. 本地测试的注意事项
使用sam local invoke或sam local start-api测试时,若已通过上述方法关联本地Layer,SAM会自动挂载Layer;若仍使用远程ARN,也可在测试命令中临时指定本地Layer路径:
sam local invoke MyFunction --layer-source NodejsLayer=../layers-stack/nodejs/
4. 部署到AWS时的恢复
部署到AWS环境时,记得将函数的Layers引用改回远程ARN,或通过CloudFormation跨栈引用已部署的Layer资源。
内容的提问来源于stack exchange,提问作者kusumoto_teruya

