Upgrading to Node.js v20 — Unlocking the Next Level of Performance and Efficiency

Anjali Malik
Naukri Engineering
Published in
6 min readMay 15, 2024
Copyright, NodeSource, Licensed Under CC-BY 4.0

In the ever-evolving landscape of web development, staying up-to-date with the latest technologies is crucial to maintaining competitiveness and ensuring optimal performance of your applications. One such pivotal technology is Node.js, the runtime environment renowned for its scalability, efficiency, and versatility in building applications.

Today, we delve into the roadmap of how to upgrade the node version and what were the challenges that we faced.

Why Upgrading Your Node Version is Essential 🤔

Security Vulnerabilities: Outdated versions of Node.js may harbor known security vulnerabilities that newer releases have patched. Running on an older version exposes your application to potential exploits.

Lack of Support: As Node.js progresses, older versions receive less support, leaving applications vulnerable to bugs without access to updates or community assistance.

Long-term Maintenance Challenges: Maintaining applications on outdated Node.js versions become increasingly complex and costly over time, requiring significant adjustments and potentially risking functionality gaps.

Dependency Management: Dependencies in Node.js applications rely on compatibility with the Node.js version. Using an outdated version may restrict the installation or update of dependencies requiring a minimum supported version.

Upgrade Strategy📈

  1. Download Node.js v20 on your system and switch Node.js version to v20
    i. Delete node_modules and package-lock.json
    ii. Run npm install command in your application.
  2. Resolve the dependency tree of private dependencies from leaf to root because the dependencies were nested as shown in below diagram.
Dependency graph of one private dependency

3. The incompatible dependencies will throw errors during installation, and replace those dependencies with the supported versions.

Error with incompatible node-sass version

Ex: In above screenshot node-sass is not supported, so we upgraded it to sass.

4. Check if all the private dependencies are compatible with Node.js v20 and can be built with it.

5. Update the private dependencies to run it on Node.js v20.

6. Update the versions of private dependencies in consumer application

7. Run npm install command.

Easy!!! Right?? But it was not that simple for us. Why??? ⤵️

Challenges Encountered During the Upgrade 🤯

Issue 1:
Addressing incompatible dependencies such as Babel, Jest when transitioning to Node.js v20.

Issue2:
We faced an issue while running npm install process, in which multiple threads of npm install command were created automatically when we were consuming private dependencies via Git URLs, necessitating a shift to managing private dependencies through an artifactory (which solved this issue for us).

Multiple npm install threads created

Similar issues are linked in the references section also.

Issue 3:
Managing compatibility problems with Node.js v16 and above in CI/CD pipelines, leading to the upgrade of the operating system (e.g., CentOS 7).

Solutions Implemented for above challenges🛠️

Solution 1:
Update all packages of consumer application to versions compatible with Node.js v20.

Solution 2:
i.In consumer application serve all private dependencies through an artifactory by configuring the artifactory URL in the .npmrc file and add “publishConfig” in package.json of private dependency to publish it to the artifactory.

.npmrc file
Artifactory URL configuration

ii. To ensure developers use the correct Node.js version while publishing the package to the artifactory set “engine-strict=true” on top of .npmrc file and specify the required Node.js version in package.json.

engine-strict=true
my-packages:registry="http://localhost:8081"
Define min node version required for application

Solution 3:
Upgrade operating system for CI/CD pipeline. We used Rocky 8.

Application configuration changes for using dependencies from artifactory:

  1. In you private dependency, to ensure that the code can be published to the artifactory you need to add
// Sample package.json
{
"version":"1.0.0",
"name" : "my-packages/package1", // scope/packageName
"publishConfig":{
"registry": "http://localhost:8081"
}
}

in your package.json, where name must contain the scope along with package name.

2. Run npm publish command, now, the version mentioned in version key of package.json will be published to the artifactory.

Note: Make sure to upgrade the version in package.json before publish or automate this by writing your own script

3. In your consumer application the URL of the artifactory (host) for private dependencies can be defined in .npmrc file (which is preferably created at project level).
Ex: .npmrc might look like:

my-packages:registry = "http://localhost:8081" //(regex for package name): registry = URL of artifactory 

Assuming all private dependencies are pushed under the scope my-packages on artifactory.
If there is no common scope, I would recommend you to create one or you have to map every single package with URL.

4. Every time you run npm install command, it checks .npmrc file where you have defined the namespace and the artifactory URL, if the namespace matches in .npmrc file that dependency will be downloaded from the URL mentioned in .npmrc else it will be served from npm registry which is default URL.

5. Consume the private package using the version published to artifactory in your consumer application.

Let us understand this with an example:

-> Say, you have a private dependency defined in package.json as:

my-internal-dependencies/some-dependency: "git+//git RepositoryUrl#v1.0.0

-> First, Go to ‘some-dependency’ code base and define publishConfig in its package.json as shown above; upgrade all versions of this package to make this package compatible with Node.js v20; also upgrade the version in package.json and then run npm publish command.

-> Create .npmrc file in your consumer application like this.

my-internal-dependencies:registry = "http://localhost:8081" //(regex for package name): registry = URL of artifactory

-> Then, update the entry of private dependency with newly created version in consumer application like:

my-internal-dependencies/some-dependency: 1.0.0

-> Run npm install command in your consumer application.

Hurray 🎉, all your private dependencies will be downloaded from the artifactory along with other dependencies from npm registry.

Additional Tips 💡

  1. You can automate the version management by writing a custom script and adding it in your application by adding this in package.json.
{ 
"scripts":{
"customCommandForPublish":"./bin/myScripts/publishScript.js"
}
}

2. Make sure you are following semver (semantic versioning) for tag creation on artifactory else it will break the npm versioning standards and might break tags with ~/^.

Note: 📝

If you are using your own script for npm publish, make sure you do not define the command name as “publish” in your scripts key because that is default command of npm and it will run the publish command twice, one as default publish and other customised command defined in scripts.

Conclusion

In summary, upgrading Node.js versions are crucial for maintaining security, compatibility, and long-term viability of applications. By addressing challenges and implementing effective solutions, the transition to newer Node.js versions can be managed effectively, ensuring continued stability and support for applications.

References:

https://github.com/npm/cli/issues/4895

https://github.com/npm/cli/issues/4028

https://github.com/npm/cli/issues/4896

--

--