esbuild can bundle your AWS Lambda functions into a single, smaller JavaScript file, dramatically reducing deployment times and improving cold starts.
Let’s see it in action. Imagine a simple Lambda function that uses the axios library to fetch data.
src/index.js
import axios from 'axios';
export const handler = async (event) => {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
return {
statusCode: 200,
body: JSON.stringify(response.data),
};
} catch (error) {
console.error('Error fetching data:', error);
return {
statusCode: 500,
body: JSON.stringify({ message: 'Failed to fetch data' }),
};
}
};
Without esbuild, if you were to package this directly, you’d include node_modules/axios and potentially many other dependencies, leading to a large deployment package.
Here’s how we’ll set up esbuild to handle this. First, you’ll need esbuild installed:
npm install --save-dev esbuild
Then, you can configure esbuild to bundle your function. A common way is to use an esbuild.config.js file:
esbuild.config.js
const esbuild = require('esbuild');
esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
platform: 'node',
target: 'node18', // Match your Lambda runtime
format: 'cjs', // CommonJS is typical for Node.js Lambda
minify: true,
sourcemap: true,
}).catch(() => process.exit(1));
When you run this config (node esbuild.config.js), esbuild will:
entryPoints: ['src/index.js']: Look at your main Lambda handler file.bundle: true: Follow allimportandrequirestatements, pulling in the code from dependencies likeaxios.outfile: 'dist/bundle.js': Write the entire bundled code, including your function and its dependencies, into a single file namedbundle.jsinside adistdirectory.platform: 'node': Ensure the output is compatible with the Node.js environment.target: 'node18': Specify the ECMAScript version and Node.js APIs your code should target, aligning with your Lambda runtime.format: 'cjs': Generate CommonJS modules, which is the standard for Node.js Lambda execution.minify: true: Strip whitespace and shorten variable names to make the output file as small as possible.sourcemap: true: Generate a source map for easier debugging, referencing your original source files.
Your deployed Lambda function would then point to dist/bundle.js as its handler.
The core problem esbuild solves is the inefficient deployment of Node.js Lambda functions that rely on external packages. Traditionally, you’d zip your function code along with the entire node_modules directory. This leads to large zip files, slow uploads to S3, and extended extraction times on the Lambda execution environment, all contributing to longer cold start durations. esbuild’s bundling process eliminates the need to ship node_modules by inlining all necessary code into a single file. This dramatically shrinks the deployment package size, often by an order of magnitude or more, directly translating to faster deployments and quicker function initialization.
When esbuild processes your code, it doesn’t just copy and paste. It performs sophisticated tree-shaking, analyzing which parts of your dependencies are actually used and omitting unused code. This is why even with a seemingly large dependency like axios, the final bundle.js can be surprisingly small. It also handles transpilation if you’re using newer JavaScript features not supported by your target Node.js version, but its primary strength lies in its speed and bundling efficiency for standard JavaScript environments.
The external option in esbuild is a powerful tool for controlling what gets bundled. By default, esbuild will bundle everything. However, if you have dependencies that are already available in the Lambda execution environment (like the aws-sdk in Node.js runtimes), you can tell esbuild to not bundle them. This can further reduce your bundle size. For example, to keep aws-sdk external:
// esbuild.config.js (snippet)
esbuild.build({
// ... other options
external: ['aws-sdk'],
// ...
});
This tells esbuild to assume aws-sdk will be present at runtime, so it won’t include its code in bundle.js.
The next step is to integrate this bundling process into your CI/CD pipeline, automating the build and deployment.