I have deployed an AWS Lambda function using Node.js 18
as the runtime and configured the handler as bundle.handler
. The code is written in TypeScript and compiled with Webpack 5
.
However, upon testing the function in the AWS console, I got the following error:
"errorType": "Runtime.HandlerNotFound",
"errorMessage": "bundle.handler is undefined or not exported"
webpack.config.ts
import { Configuration } from "webpack";
import { resolve } from "path";
const config: Configuration = {
name: "bundle",
mode: "none",
entry: {
bundle: "./src/main.ts",
},
target: "node",
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: {
loader: "ts-loader",
options: {
configFile: "tsconfig.json",
},
},
},
],
},
resolve: {
modules: ["node_modules"],
extensions: [".tsx", ".ts", ".js", ".json"],
alias: {
"@": resolve(__dirname, "../code/src"),
},
},
output: {
filename: "[name].js",
path: resolve(__dirname, "dist"),
globalObject: "this",
library: {
name: "bundle",
type: "commonjs2",
},
},
};
export default config;
main.ts fragment
export const handler: Handler = async (event, context): Promise<any> => {
...
return context.logStreamName;
};
bundle.js fragment
const handler = (event, context) => __awaiter(void 0, void 0, void 0, function* () {
...
return context.logStreamName;
});
exports.handler = handler;
...
/******/ // module cache are used so entry inlining is disabled
/******/ // startup
/******/ // Load entry module and return exports
/******/ var __webpack_exports__ = __webpack_require__(__webpack_require__.s = 0);
/******/ module.exports.bundle = __webpack_exports__;
Any pointers in the right direction would be greatly appreciated!
When using webpack's output.library.type
with a value of commonjs
the built library is exposed to consumers as such:
The return value of your entry point will be assigned to the exports object using the
output.library.name
value. As the name implies, this is used in CommonJS environments.exports['MyLibrary'] = _entry_return_; require('MyLibrary').doSomething();
You are running into issues because you are also using an object for entry
which produces chunks named after their associated keys, in this case bundle
, the same name used for your library.
According to the AWS docs for a lambda handler function in Node.js
When you configure a function, the value of the handler setting is the file name and the name of the exported handler method, separated by a dot.
So when the lambda runtime attempts to load your handler it will call the bundle file name (bundle
) and the name of your handler (handler
), separated by a dot: bundle.handler
. But your library build is named bundle
, so your handler will be exposed as function of that namespace: bundle.bundle.handler
.
An easy solution with your current configuration would be to not use the output.library.name
and let your named entry export of handler
be directly attached to the exports
object under no namespace. There is a warning when in the webpack docs when not using output.library.name
:
Note that not setting a
output.library.name
will cause all properties returned by the entry point to be assigned to the given object; there are no checks against existing property names.
Thanks for the warning, noted, but in this case the runtime expects the exports to be directly attached based on the name already given to your bundle, and naming collisions are not anticipated, so removing the library name is acceptable.
There are other ways to produce a webpack build that is supported by the AWS lambda runtime, and uses output.library.name
. In those other cases you most likely would not also use an object for your entry
, but rather a string or array of strings, and then explicitly set output.filename
to your library's name.