Introduction Link to heading
In Getting Started with CDK,
I wrote “I noticed that cdk has a pattern of using a lambdas directory
and then having sub-directories for every lambda”.
Well, I was wrong, or maybe things have just changed.
Either way, we’re going to update hugo-cdk to follow the new pattern.
The New Pattern Link to heading
Recently, cdk was updated to add bundling support. We didn’t need bundling with our previous lambda, because it doesn’t depend on any libraries. The new pattern is still a more elegant approach, so lets take a look at the fancy but plainly named aws-lambda-nodejs.
Before:
this.fn = new lambda.Function(this, 'RedirectLambda', {
functionName: 'cloudfront-redirect',
runtime: lambda.Runtime.NODEJS_12_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, 'lambdas', 'cloudfront-redirect')),
role: role,
});
After:
this.fn = new lambda.Function(this, 'cloudfront-redirect', {
runtime: lambda.Runtime.NODEJS_12_X, // not strictly require, but...
role: role,
});
That looks like it will let me delete more than half the code! Very promising! “Convention over configuration” is pretty persuasive, so…
Preparation Link to heading
Under the hood, the bundler is going to use parcel. If you don’t have a local install of it, cdk will cleverly use docker to just make things work. Unfortunately, this is slow beyond words if you happen to be on a mac. Fortunately, cdk will detect and use a local install, which is much faster.
npm install --save-dev --save-exact parcel@2.0.0-beta.1
Implementation Link to heading
As usual, we start by upgrading cdk. Next, we’ll need to pull in the new bundler module:
npm install @aws-cdk/aws-lambda-nodejs
We’ll start by renaming the lambda to it’s new standard location.
cd lib;
git mv lambdas/cloudfront-redirect/index.ts cloudfront-redirect.cloudfront-redirect.ts
And then updating the code. Here’s a diff to make it easier to see the change.
[andrew.hammond@andrew-hammond-C02XKGFMJG5H-SM personal hugo-cdk (master $%)]$ git show -p
commit b070d451df8a52d09db1e03e7496647cc2dc17d6 (HEAD -> master)
Author: Andrew Hammond <andrew.hammond@helix.com>
Date: Sun Oct 4 18:54:35 2020 -0700
lambda-nodejs pattern
diff --git a/lib/lambdas/cloudfront-redirect/index.ts b/lib/cloudfront-redirect.cloudfront-redirect.ts
similarity index 95%
rename from lib/lambdas/cloudfront-redirect/index.ts
rename to lib/cloudfront-redirect.cloudfront-redirect.ts
index a716a4d..1363b87 100644
--- a/lib/lambdas/cloudfront-redirect/index.ts
+++ b/lib/cloudfront-redirect.cloudfront-redirect.ts
@@ -1,4 +1,4 @@
-import awsLambda = require('aws-lambda');
+import * as awsLambda from 'aws-lambda';
function generateRedirectResponse(value: string): awsLambda.CloudFrontRequestResult {
return {
diff --git a/lib/cloudfront-redirect.ts b/lib/cloudfront-redirect.ts
index ebbec1d..df102cb 100644
--- a/lib/cloudfront-redirect.ts
+++ b/lib/cloudfront-redirect.ts
@@ -1,7 +1,7 @@
import cdk = require('@aws-cdk/core');
import iam = require('@aws-cdk/aws-iam');
import lambda = require('@aws-cdk/aws-lambda');
-import path = require('path');
+import lambdaNodejs = require('@aws-cdk/aws-lambda-nodejs');
export class CloudfrontRedirect extends cdk.Construct {
public readonly fn: lambda.Function;
@@ -21,12 +21,16 @@ export class CloudfrontRedirect extends cdk.Construct {
}),
);
- this.fn = new lambda.Function(this, 'RedirectLambda', {
- functionName: 'cloudfront-redirect',
- runtime: lambda.Runtime.NODEJS_12_X,
- handler: 'index.handler',
- code: lambda.Code.fromAsset(path.join(__dirname, 'lambdas', 'cloudfront-redirect')),
+ this.fn = new lambdaNodejs.NodejsFunction(this, 'cloudfront-redirect', {
+ runtime: lambda.Runtime.NODEJS_12_X, // Default would work, but let's go with latest greatest.
role: role,
+ parcelEnvironment: {
+ NODE_ENV: 'production',
+ },
+ externalModules: [
+ 'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
+ ],
+ nodeModules: ['@types/aws-lambda'],
});
new cdk.CfnOutput(this, 'TheRedirectLambda', { value: this.fn.functionArn });
And finally I set the parcelEnvironment, which… I’m totally guessing is important. I like this new pattern.
Things worth noting:
- I did indeed get to remove a bunch of boilerplate (the
functionName,code, andhandler) in favor of convention. - I also made explicit the sourcing of the modules being used.
This code was taking advantage of the fact that
aws-sdkis already present, and that theaws-lambdacode is only used for type information. Now we can reference out to other code - We had to get rid of the
import foo = require('...');in favor ofimport * as foo from '...';. I haven’t found a clear explanation of the difference between these two import patterns. So I’ve been using therequire()pattern because… uh… it looked more elegant to me. The fact thatparceldoesn’t like it though? That’s a strong enough argument for me to switch. - This will change the name of the resource we have used to deploy the lambda, so we’ll need to re-deploy both of our stacks.
Cleanup Link to heading
I see that I have a new directory in my hugo-cdk path:
[andrew.hammond@andrew-hammond-C02XKGFMJG5H-SM master hugo-cdk (master $%)]$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
(use "git push" to publish your local commits)
Untracked files:
(use "git add <file>..." to include in what will be committed)
.parcel-cache/
A quick browse of the parcel docs tells me that this belongs in .gitignore
echo '.parcel-cache/' >> .gitignore
git add .gitignore
git commit -m "ignore the .parcel-cache"
Conclusion Link to heading
I like this new pattern. I LOVE that it will simplify the deployment of lambdas which require bundled libraries. Catch you next post!