Angular CLI Builders for AWS SAM projects, designed for use alongside nx
nx superpowers the angular CLI, to add support for a range of backend project types.
However, what if your backend uses SAM?
This project includes builders for that!
- @nx-aws/sam:build - builds your functions
- @nx-aws/sam:package - packages your SAM (ie. CloudFormation) template ready for deployment (including resolving AWS::Serverless::Application references to other apps in your monorepo)
- @nx-aws/sam:deploy - deploys your CloudFormation template
NB: nx-aws 0.10.0 and higher require @nrwl/nx v11 and @angular-devkit/core v11.
- Open your existing workspace or run npx create-nx-workspaceto create a new workspace
- npm install @nx-aws/samor- yarn add @nx-aws/sam
- nx g @nx-aws/sam:app api [--frontendProject sample]
- Create a bucket in AWS to store deploy artifacts (via the console or AWS CLI using aws s3api create-bucket --bucket ${my-nx-deploy-artifacts} --region us-east-1)
- Update your workspace.jsonorangular.jsonto include the keys3Bucketunder both thepackageanddeploytargets (see details below).
Add the following to your angular.json
{
    "api": {
        "root": "apps/api",
        "sourceRoot": "apps/api/src",
        "projectType": "application",
        "prefix": "api",
        "schematics": {},
        "architect": {
            "build": {
                "builder": "@nx-aws/sam:build",
                "options": {
                    "outputPath": "dist/apps/api",
                    "template": "apps/api/template.yaml",
                    "tsConfig": "apps/api/tsconfig.app.json"
                },
            ...
            }
        }
    }
}The builder will search through your CloudFormation template at apps/api/template.yaml
and find any AWS::Serverless::Function and trigger appropriate builds.
(All the other options are the same as for nrwl's node builder.)
Given this code in your template.yaml:
Resources:
    MyFunction:
        Type: 'AWS::Serverless::Function'
        Properties:
            # CodeUri should be the directory, relative to template.yaml, where the handler file is found
            CodeUri: src/my-function
            # This is the name of the handler file and then the name of the exported handler function
            # (standard SAM approach)
            Handler: handler-file.handlerFnThe builder will run a webpack build for src/my-function/handler-file.
There's an experimental builder added for lambda layers, @nx-aws/sam:layer. It wraps the tsc executor,
with all the same options.
After the tsc executor has run, it creates a package.json file, runs npm install with appropriate flags for AWS Lambda, and then zips the result. You can then deploy this
using the package and deploy executors.
This is the resource you'll need in your template.yaml:
AirmailLayer:
    Type: AWS::Serverless::LayerVersion
    Description: Airmail layer
    Properties:
        ContentUri: ./nodejs.zip
        CompatibleRuntimes:
            - nodejs14.x
            - nodejs16.xNote: At the moment, the ContentUri ./nodejs.zip is essentially hard-coded. The assumption is, essentially, that the layer is the only resource in this template.
My preferred way to use the layer is via the importStackOutputs on another stack.
Lambda layers defined in your template should Just Work - however, the way sam-cli treats layers is broken, so they won't: aws/aws-sam-cli#2222.
That said, if you've got a layer defined like this:
  TestLayer:
    Type: AWS::Serverless::LayerVersion
    Description: Test layer
    Properties:
      ContentUri: ./src/test-layer
      CompatibleRuntimes:
        - nodejs10.x
        - nodejs12.x
    Metadata:
      BuildMethod: nodejs12.x
Then during serve or build nx-aws will simply map the ContentUri to an absolute path. Assuming you've got a
layer at that location that sam-cli is happy with, then you're good to go.
Add the following to your angular.json:
{
    "api": {
        "root": "apps/api",
        "sourceRoot": "apps/api/src",
        "projectType": "application",
        "prefix": "api",
        "schematics": {},
        "architect": {
            "package": {
                "builder": "@nx-aws/sam:package",
                "options": {
                    "templateFile": "apps/api/template.yaml",
                    "outputTemplateFile": "dist/apps/api/serverless-output.yaml",
                    "s3Prefix": "api",
                    "s3Bucket": "my-artefacts-bucket"
                },
                "configurations": {
                    "production": {}
                }
            }
        }
    }
}NB: sam:package requires an S3 bucket to store deploy artefacts - you need to create a bucket and add the s3Bucket option to your project configuration
For the most part, this simply wraps the aws cloudformation package command, but it will also
rewrite the Location property of AWS::Serverless::Application resources, if they refer to
another project.
The package builder will attempt to resolve a reference to another CloudFormation stack, defined
in a different project in angular.json.
If the package builder finds an AWS::Serverless::Application in template.yaml, eg:
Resources:
    MySubStack:
        Type: AWS::Serverless::Application
        Properties:
            Location: my-sub-stackit will attempt to:
- Find an project in angular.jsonthat matches theLocationproperty, ie.my-sub-stack.
- If it finds such a project, it will look for the packagetarget.
- If it finds the packagetarget, it will replacemy-sub-stackwith the absolute path to theoutputTemplateFilefrom that target.
Add the following to angular.json:
{
    ...
    "api": {
        "root": "apps/api",
        "sourceRoot": "apps/api/src",
        "projectType": "application",
        "prefix": "api",
        "schematics": {},
        "architect": {
            ...
            "deploy": {
                "builder": "@nx-aws/sam:deploy",
                "options": {
                    "templateFile": "dist/apps/api/serverless-output.yaml",
                    "s3Prefix": "api",
                    "capabilities": ["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"],
                    "s3Bucket": "my-artefacts-bucket",
                    "stackNameFormat": "api-$ENVIRONMENT"
                },
                "configurations": {
                    "production": {}
                }
            }
        }
    }
}NB: sam:deploy requires an S3 bucket to store deploy artefacts - you need to create a bucket and add the s3Bucket option to your project configuration - this must be the same as used for sam:package
This wraps the aws cloudformation deploy command. The one nice thing it does is pull
any parameters defined in your template.yaml from environment variables, and pass them
in as parameter overrides. For example, if you have in your template.yaml:
Parameters:
    MyParameter:
        Type: String
        Description: An example parameterThe the deploy builder will look for an environment variable MY_PARAMETER and pass it in as a parameter overrides.
The SAM package and deploy steps require an s3 bucket to store and retrieve deployment artefacts.
You need to create and s3 bucket to store your deployment artefacts and then include that bucket in your project configuration.
- Create a bucket in AWS to store deploy artifacts (via the console or AWS CLI using aws s3api create-bucket --bucket ${my-nx-deploy-artifacts} --region us-east-1)
- Update your workspace.jsonorangular.jsonto include the keys3Bucketunder both thepackageanddeploytargets (see details above for the package and deploy steps).
PRs and contributions are very very welcome!
To build, run yarn build.
yarn link doesn't work to test locally, due to the way npm resolves dependencies. The best
workflow I've found is to copy across the files as the change. There's a script to do
this: yarn pack:copy --projectPath ../test-nx-aws/ - just change ../test-nx-aws to your
local test project.