In a recent consultancy project, I was tasked with creating a mock environment for an asynchronous webservice using LocalStack, with the primary aim of minimizing the feedback loop associated with integrating our systems. This involved using AWS CDK
to provision multiple TypeScript runtime lambdas in LocalStack and esbuild
to build the functions.
On completing the mock environment, I decided to go the extra mile by attempting to enable hot reloading for the lambdas, a feature that proved more challenging than expected, as outlined in the official LocalStack guide.
After navigating through moments of frustration, fueled by a few cups of Yorkshire tea, I eventually uncovered the solution to hot-reloading multiple TypeScript lambdas in LocalStack. Below, I have outlined guide on how to achieve this for others navigating similar challenges.
Before diving into this guide, it's recommended to familiarize yourself with the official hot-reloading Localstack guide for a single Lambda function.
The following steps provide a summary of the official guide for hot-reloading TypeScript Lambda functions in LocalStack:
-
The Build Step:
package.json
"scripts": { "build": "esbuild index.ts \ --bundle --minify --sourcemap \ --platform=node --target=es2020 \ --outfile=dist/index.js --watch" },
Executing the above build script withnpm run build
uses esbuild to bundle and minify the TypeScript code into a single JavaScript file, stored in thedist
folder. Deploying to LocalStack
awslocal lambda create-function \
--function-name hello-world \
--runtime "nodejs16.x" \
--role arn:aws:iam::123456789012:role/lambda-ex \
--code S3Bucket="hot-reload",S3Key="$(PWD)/dist" \
--handler index.handler
If followed correctly, these steps should enable hot-reloading for a single Lambda function after every code change.
Hot-Reloading Multiple Lambdas
To extend hot-reloading to multiple Lambdas, a key modification is needed at the build step:
"scripts": {
"build": "esbuild lambdas/src/*.ts
--bundle --minify --sourcemap \
--platform=node --target=es2020 \
--outdir=lambdas/build --watch"
},
This command individually transpiles, bundles, and minifies each Lambda entry file in the lambdas/src
folder, placing the output in the lambdas/build
directory.
Note: The lambdas/src
folder should match the directory where your Lambdas reside. Other bundlers, not just esbuild, can be used for the build step.
Proceed to deploy each Lambda to Localstack using the awslocal lambda create-function
command, and they should hot-reload with every code change.
Important: When deploying from within a container, ensure that the S3key
is a path on the host, as Localstack maps directories from the host to the Lambda containers.
Bonus Information: AWS Cloud Development Kit (CDK) Configuration for Hot-Reloaded Lambda.
import { NodejsFunction, NodejsFunctionProps } from 'aws-cdk-lib/aws-lambda-nodejs';
import { Architecture, Code, Function, Runtime } from "aws-cdk-lib/aws-lambda";
import { Bucket } from "aws-cdk-lib/aws-s3";
import { Construct } from "constructs";
import { join, basename } from "path";
const STAGE = process.env.STAGE ?? "local";
const LAMBDA_MOUNT_CWD = join(__dirname, "lambdas/build")
function ApplicationFunction(
scope: Construct,
id: string,
props: NodejsFunctionProps
) {
if (STAGE === "local") {
return LocalFunction(scope, id, props);
}
return new NodejsFunction(scope, id, props);
}
function LocalFunction(
scope: Construct,
id: string,
props: NodejsFunctionProps
) {
const hotReloadBucket = Bucket.fromBucketName(
scope,
`HotReloadingBucket-${id}`,
"hot-reload"
);
if (!props.entry) throw new Error('Entry point is required');
const fileName = basename(props.entry, ".ts");
const handler = props.handler ?? "handler";
const runtime = props.runtime || Runtime.NODEJS_18_X;
return new Function(scope, id, {
...props,
code: Code.fromBucket(hotReloadBucket, join(__dirname, "lambdas/build")),
runtime,
handler: `${fileName}.${handler}`,
});
}
Bootstrap and deploy to LocalStack by running:
cdklocal bootstrap && cdklocal deploy --require-approval never
For a complete working example of hot-reloading multiple TypeScript Lambdas, refer to this example CRUD application with five hot-reloaded Lambdas.
May your Lambdas always hot-reload!
Top comments (0)