When I started putting together my first AWS SAM project, I was confused with the project structure - as always, when I’m starting new project with new technology. You can easily make a bloated project where code is duplicated in each lambda function.
I had a few goals to achieve:
simple and general structure which is a good starting point for smaller applications
each lambda function has its own dependencies
common code is shared in layers
no external dependencies in the repository - every dependency is defined in package.json
In the project folder, I created folder Lambdas. Each lambda is in its own folder with package.json. The package.json contains dependencies only required by this function.
Folder Layers will contain folders with layers. Because the structure is just a starting point, I will have only two layers. One called SharedInternalDependencies with my own library code which I want to share and one called SharedExternalDependencies with third-party libraries used in lambdas and in internal shared dependencies.
SharedExternalDependencies just contain package.json. Dependencies will be downloaded by SAM during the build process. Yes, SAM can build not only lambdas but also layers. But you should use the newest version of SAM, because this feature wasn’t always there.
SharedInternalDependencies is not built by SAM, so we have to create a valid folder structure for Node. The correct structure is nodejs/node_modules/my_module. Or nodejs/node_modules/my_module.js. If you use a different language, the path has to be different. You can find the correct paths in the AWS documentation.
The final structure will look like this:
MySAMProject
template.yml
Lambdas
Lambda-1
lambda-1.js
package.json
Lamda-2
lambda-2.js
package.json
Layers
SharedInternalDependencies
nodejs
node_modules
my-library-module.js
SharedExternalDependencies
package.json
The last step is to configure lambdas and layers in template.yml. My example will have these 4 resources.
lambda1:
Type: AWS::Serverless::Function
Properties:
CodeUri: Lambdas/Lambda-1/
Handler: lambda-1.myExportedFunction
Runtime: nodejs12.x
MemorySize: 128
Timeout: 100
Description: My first lambda function.
Layers:
- !Ref externalDep
- !Ref internalDep
lambda2:
Type: AWS::Serverless::Function
Properties:
CodeUri: Lambdas/Lambda-2/
Handler: lambda-2.myExportedFunction
Runtime: nodejs12.x
MemorySize: 128
Timeout: 100
Description: Second lambda.
Layers:
- !Ref externalDep
- !Ref internalDep
externalDep:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: SharedExternalDependencies
Description: Shared external dependencies.
ContentUri: Layers/SharedExternalDependencies/.
CompatibleRuntimes:
- nodejs12.x
RetentionPolicy: Retain
Metadata:
BuildMethod: nodejs12.x
internalDep:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: SharedInternalDependencies
Description: Shared internal dependencies.
ContentUri: Layers/SharedInternalDependencies/.
CompatibleRuntimes:
- nodejs12.x
RetentionPolicy: Retain
Notice the definition of external layer has Metadata. The selected BuildMethod will build the layer. If you open .aws-sam, you can notice that the correct path nodejs/node_modules with downloaded modules was created.
Comments