Specifying the aws-sdk version in aws-cdk NodejsFunction

Posted on

AWS Cloud Development Kit (CDK) makes it super-easy to deploy Node.js Lambda functions using the NodejsFunction construct.

However, one issue I’ve seen with this construct is that, by default, the aws-sdk version defined in your package.json file is not bundled with the Lambda that the CDK publishes. For the TLDR, jump below.

Consider a NodejsFunction defined like this:

// lib/index.ts
import * as cdk from "@aws-cdk/core";
import * as nodelambda from "@aws-cdk/aws-lambda-nodejs";
import { join } from "path";

export default class MyLambdaStack extends cdk.Stack {
  constructor(scope: cdk.Construct) {
    super(scope, "MyLambdaStack");

    new nodelambda.NodejsFunction(this, "Lambda", {
      entry: join(__dirname, "lambda", "index.ts"),
    });
  }
}

This lambda has a package.json file that looks like this:

// lib/lambda/package.json
{
  "name": "lambda",
  "version": "1.0.0",
  "dependencies": {
    "aws-sdk": "2.935.0"
  }
}

It’s reasonable to assume that, at runtime, my Lambda function would use aws-sdk version 2.935.0, as I’ve defined. However, by default, the NodejsFunction bundler considers aws-sdk to be an “external module” provided by the Lambda runtime (docs).

screenshot of the aws-cdk bundling options documentation page

So, instead of the version you’ve defined in your package.json file, the version of aws-sdk included in the Lambda runtime is used instead. At the time this blog post was written, it was 2.888.0. You can find the most up-to-date information on the bundled aws-sdk version in the Lambda Runtimes documentation.

screenshot of the bundled aws-sdk version available in the Node.js lambda runtime

In most cases, this discrepancy doesn’t matter. However, if you need to use a feature of the aws-sdk that’s only available in newer versions, or you need to pin to a specific version for some other reason, this default behavior can be undesirable.

To work around this problem, override the externalModules setting in your NodejsFunction declaration. For example:

// lib/index.ts
import * as cdk from "@aws-cdk/core";
import * as nodelambda from "@aws-cdk/aws-lambda-nodejs";
import { join } from "path";

export default class MyLambdaStack extends cdk.Stack {
  constructor(scope: cdk.Construct) {
    super(scope, "MyLambdaStack");

    new nodelambda.NodejsFunction(this, "Lambda", {
      entry: join(__dirname, "lambda", "index.ts"),

      bundling: {
        // This is the configuration you need to use to include the exact
        // aws-sdk version from your `package.json` file.
        externalModules: [],
      },
    });
  }
}

Now, the bundling process will install the aws-sdk version defined in your package.json file. Note that your resulting lambda will be larger in size since the aws-sdk is included in the final asset.

Summary / TLDR

If you need to include a version of the aws-sdk package that’s newer than the one included in the Node.js Lambda Runtime you’re using, specify an empty array for the externalModules bundling options property (docs).

new NodejsFunction(this, "Lambda", {
  // ... other config ...
  bundling: {
    externalModules: [],
  },
});

This configuration ensures that the exact aws-sdk module defined in your package.json file is included in the uploaded Lambda function.

Find an issue?
Open a pull request against my blog on GitHub.
Ben Limmer