Python AWS CDK Pipelines with NodeJS Dependencies
Tags: aws, cdk, node, python
Published: 2023-09-27 04:17:04

[AWS CDK Pipelines](https://docs.aws.amazon.com/cdk/v2/guide/cdk_pipeline.html) are a fantastic way to deploy your infrastructure using CI/CD practices.

The pipeline, using AWS CodeBuild and CodePipeline, can reproducibly build and deploy both your front-end and back-end applications. But how can we use Python to build the pipeline, but then deploy Lambda Functions written in `JavaScript` or `TypeScript`?

This has been a bit of a tricky problem for me, because most examples found online are for `AWS CDK` projects written in `TypeScript`. We need to add an additional step to our pipelines if we want our NodeJS Lambda Functions to get built with dependencies.

TLDR: Simply run `npm i` in your shell step and your dependencies detailed in your root `package.json` file will be available when CodeBuild is building your NodeJS functions.

With AWS CDK Pipelines, there is no need to specify dependencies in each individual function folder. Instead, specify all of them at the root of your project in a `package.json` file. When `npm i` is run in the pipeline's shell step, these dependencies become available for all of your NodeJS functions to build with.

I provide a full example below.

Let's build our stack, stage, and pipeline in Python flavored AWS CDK!

## STACK WITH NODEJS LAMBDA FUNCTION
```python
# stacks/backend_stack.py
from constructs import Construct
from aws_cdk import aws_lambda_nodejs as _lambda_nodejs
from aws_cdk import Stack

class BackendStack(Stack):
    def __init__(
        self, scope: Construct, construct_id: str, **kwargs
    ) -> None:
        super().__init__(scope, construct_id, **kwargs)

        self.backend_lambda = self.build_backend_lambda()

    def build_backend_lambda():
            """
            Example NodeJS lambda
            """
            # Here I instantiate a lambda function which is written in Javascript or Typescript. The file app.js exports a function called handler.
            backend_lambda = _lambda_nodejs.NodejsFunction(
                scope=self,
                id="backendLambda",
                runtime=_lambda.Runtime.NODEJS_16_X,  # type: ignore
                entry="lambda_funcs/backend/app.js",
                handler="handler",
            )


```

## STAGE
```python
# app.py
from constructs import Construct
from stacks.backend_stack import BackendStack

class BackendStage(Stage):
    def __init__(self, scope: Construct, id: str, **kwargs):
        super().__init__(scope, id, **kwargs)
        )
        self.backend = BackendStack(
            scope=self, construct_id="Backend"
        )
```

## PIPELINE
```python
# app.py
from aws_cdk import aws_secretsmanager as secretsmanager
from aws_cdk.aws_codebuild import (
    BuildEnvironment,
    ComputeType,
    LinuxBuildImage,
)
from aws_cdk.aws_codepipeline_actions import GitHubTrigger
from constructs import Construct

class PipelineStack(cdk.Stack):
    def __init__(
        self,
        scope: Construct,
        construct_id: str,
        **kwargs,
    ) -> None:
        super().__init__(scope, construct_id, **kwargs)
        github_secret = secretsmanager.Secret.from_secret_name_v2(
            scope=self,
            id="github_secret",
            secret_name="Github_Personal_Access_Token",
        )
        code_build = pipelines.CodeBuildOptions(
            build_environment=BuildEnvironment(
                build_image=LinuxBuildImage.STANDARD_5_0,
                compute_type=ComputeType.SMALL,
            )
        )
        # This pipeline deploys a cdk stack
        pipeline = pipelines.CodePipeline(
            scope=self,
            id="Pipeline",
            code_build_defaults=code_build,
            synth=pipelines.ShellStep(
                id="Synth",
                input=pipelines.CodePipelineSource.git_hub(
                    repo_string="USERNAME/REPOSITORY",
                    branch="dev",
                    authentication=github_secret.secret_value_from_json(
                        key="github_access_token"
                    ),
                    trigger=GitHubTrigger.WEBHOOK,
                ),
                commands=[
                    "pip install -r requirements.txt",
                    "npm i",  # This is the essential step for this article
                    "npm install -g aws-cdk",
                    "cdk synth",
                ],
            ),
            docker_enabled_for_synth=True,
        )

        pipeline.add_stage(
            BackendStage(scope=self, id="backend"),
        )
```

Good luck!