Skip to content

Fixes and improvements for current AWS-provided Node runtimes - #155

Open
ScottMorse wants to merge 20 commits into
arithmetric:masterfrom
ScottMorse:master
Open

ScottMorse wants to merge 20 commits into
arithmetric:masterfrom
ScottMorse:master

Conversation

@ScottMorse

@ScottMorse ScottMorse commented Apr 6, 2026

Copy link
Copy Markdown

Issues

Fixes (#154)

Main issues

  • The latest AWS-provided Node 24 runtime no longer supports the callback-based handler used currently, requiring async handlers instead. Async handlers have been supported since 2018 and are supported on all provided runtimes. AWS Docs: Valid handler patterns for Node.js functions
  • For users copying the contents of index.js into the Lambda console for a quick setup, Lambda now defaults to using index.mjs, so the CJS syntax breaks unless one knows to update the handler filename.
    • ESM syntax is now highly encouraged in Lambda and Node in general and would make use of this code smoother for all the current supported Node runtimes, which are 20, 22, and 24. ESM syntax has been supported in Lambda since the Node 14 runtime.

Related/secondary issues

  • Lambda looks at the handler function .length to detect a callback vs. async handler, so the overrides must be split from the main async handler function in addition to dropping the callback param.
  • ESM syntax isn't very friendly with nyc for tests
  • The version logged at the top of index.js is inaccurate from being hard-coded from a previous version

Solutions

  • Update index.js functions to be async arrow functions as the fix for Node 24
  • Use createHandler to split overrides param out of handler
    • Lambda reads handler.length to detect callback vs. async handler use, so it will still throw a deprecation error in Node 24 without this, even if correctly using the async pattern.
  • Update example to use createHandler
  • Update tests
    • Use async syntax for updated index.js functions
    • Use createHandler for overrides
    • Preserve original intent as closely as possible
    • Minor change to impl of final handler.js test to use explicitly rejected Promise, instead of causing unobvious TypeError via next().
  • Use ESM syntax
    • Update index.js, example/, and tests/ imports and exports
    • Add "type": "module" to package.json
    • Switch sourceType to "module" in ESLint config
    • Remove "use strict" since this is the default for ESM
  • Switch to c8 for tests instead of nyc
    • ESM-friendly with similar features
    • Updated package.json scripts to approximate old nyc usage
  • Upgrade AWS SDK dependencies (will match what is available by default on a Lambda instance more closely)
  • Fix console.log in index.js to reflect current package version (would need to be updated again on version bump)

Notes

  • I'm using this code currently with Node 24
  • Tests and lint pass for me locally
  • I've manually tested this with the two other supported runtimes, Node 20 and 22
  • While ESM has been around for a while and is encouraged by Lambda, this could break CJS users of the library. I'd argue the best solution overall might be to add something like a build step that can output CJS from the raw ESM for maximum compatibility, but since I bit off a lot just with this PR, I did not want to tackle something like this at the moment.

brandonbothell

This comment was marked as outdated.

@brandonbothell brandonbothell left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you rename index.js to index.mjs?

@ScottMorse

Copy link
Copy Markdown
Author

@brandonbothell Since package.json would now have "type": "module", index.js is treated as ESM already. It would reflect what users see in the Lambda editor in the AWS console a bit more to change the name if they're using a copy+paste setup, but it would mostly be an aesthetic change. Up to you. It would just mean changing the file name and updating the "main" field in package.json.

@brandonbothell

brandonbothell commented Apr 17, 2026 via email

Copy link
Copy Markdown

@ScottMorse

Copy link
Copy Markdown
Author

@brandonbothell Makes sense! I went ahead and performed the update:

  • file name change
  • "main" field change in package.json
  • README and comment in example now refer to index.mjs instead of index.js
  • ESLint config covers .mjs
  • Tests needed import statements updated

I also just noticed that the README lists Node 18, 20 and 22 as the current Lambda runtimes, so I updated this to 20, 22 and 24, and the lint-test github workflow similarly runs against Node 18, 20, and 22, so I updated that as well.

Comment thread package.json Outdated
"main": "index.mjs",
"scripts": {
"check-coverage": "c8 report --reporter=lcov && c8 check-coverage --statements 100",
"lint": "npx eslint",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be updated to npx eslint@^9.18.0 (or we can update dependencies?)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, npx is actually redundant here. package.json scripts have node_modules/.bin in their PATH, which is why you can invoke dependencies' executables there directly.

npx will similarly use node_modules/.bin if it's present from the cwd. If npx is called and the command isn't found in node_modules/.bin, that's when it installs the latest version of a package dynamically (or uses its cache) to run it.

So in this instance, npx eslint is no different than plain eslint within "scripts".

The "eslint" package is installed as a peer dependency of the other ESLint-related packages, which supplies the eslint command and may differ in version from @eslint/js.

I am seeing now that when I delete my package-lock.json and run npm install, it downloads eslint at version 10.2.0, so running npx eslint --version in the repo then reports v10.2.0 to me, the same as if I run npm run lint -- --version regardless of whether npx eslint or eslint is used in the package.json script.

However, version 10 doesn't seem to be working with the plugins being used, so it's probably worth locking down the version of "eslint" to use to that it's not up to chance anymore. I can make a simple change for all this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this makes sense as why I had to pin the npx command @^9.18.0. Thanks again for the useful info, your suggestion sounds good!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem! I author a package that has a CLI for Bun, and package.json, node_modules/.bin, and bunx (the npx equivalent) work essentially the same way, which is the only reason I actually understand how this stuff works.

I went ahead and made the package.json changes for this, just setting the "eslint" version and simplifying the script to eslint.

@brandonbothell

brandonbothell commented Apr 17, 2026 via email

Copy link
Copy Markdown

@ScottMorse

Copy link
Copy Markdown
Author

@brandonbothell I don't think that's the case. npx eslint still defaults to node_modules/.bin/eslint regardless of whether a global install of eslint is present. When you use npm i -g eslint, you do have the eslint executable on your user's PATH, but the node_modules/.bin path still takes precedence over that for both npx or the plain eslint script.

I have a feeling it's probably just that the author used npx eslint to run it before adding the script in package.json and didn't realize or forgot that it's unnecessary.

@brandonbothell

brandonbothell commented Apr 17, 2026 via email

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants