Automating and optimizing npm login for scale

Kim T
Creative Technology Concepts & Code
3 min readJun 23, 2021

It is common for companies to have their own private npm registries to store proprietary code. Developers need to set the registry and log in to ensure npm is able to publish and install packages. The two main commands used are:

1) Set your company’s npm Enterprise registry as the default

npm config set registry https://registry.your-registry.npme.io/

2) Log in with a scope configured to point at your registry

npm login --registry=https://registry.company-name.npme.io

If you are using AWS CodeArtifact you can run a single helper command:

aws codeartifact login --tool npm --repository my-repo --domain my-domain

Automating commands

We want to make life easier for developers, automating commands where possible. We can add them to the package.json as a custom script command:

{
"name": "private-npm",
"version": "1.0.0",
"description": "Private npm demo",
"main": "index.js",
"scripts": {
"login": "npm config set registry https://registry.your-registry.npme.io/ && npm login --registry=https://registry.company-name.npme.io"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {}
}

This allows developers to run npm run login and then npm install to auth and install packages from the private registry.

Taking it one step further we can tie into npm lifecycle scripts to run the login command automatically:

{
"name": "private-npm",
"version": "1.0.0",
"description": "Private npm demo",
"main": "index.js",
"scripts": {
"login": "npm config set registry https://registry.your-registry.npme.io/ && npm login --registry=https://registry.company-name.npme.io",
"preinstall": "npm run login",
"prepare": "npm run login"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {}
}

This allows developers and CI pipelines to run npm install or npm publish and the auth is handled for them. Great!

However there is a problem at scale. Every package and dependency packages run the commands, updating the registry url and authenticating recursively n times.

In addition running the login command is expensive, taking 3.135 seconds

time npm run login

We want to reduce this time and optimize for scale/recursive calls.

Performance optimizations

1) Check whether we can already access the registry

Invoking the npm ping command is an easy way to verify whether we have access to the private registry.

You can chain bash commands, ensuring login only runs if the ping fails:

npm ping --registry "https://registry.your-registry.npme.io/" || npm run login

If you time the output of the commands this takes: 0.686 seconds. That’s a reduction of 2.5 seconds per call! Great, can we do even better?

2) Check when the auth token is due to expire

If the token has a fixed expiry date (AWS CodeArtifact is 12 hours), we store the date when we login, and check whether we have passed the expiry time

"login:once": "if [[ $(date +%s) -ge $AUTH_EXPIRES ]]; then npm run login && AUTH_EXPIRES=$(date -v+720M +%s); fi"

If you time the output of the commands this takes: 0.005 seconds. That’s a reduction of 3.1 seconds per call!

How “login:once” works

When we login, we save the datetime the token is due to expire to a variable AUTH_EXPIRES=$(date -v+720M +%s) Current date plus 12 hours (12 hours x 60 mins = 720M)

Every time the command is run, we check whether the current date is greater than the expiry datetime $(date +%s) -ge $AUTH_EXPIRES

package.json for private npm

{
"name": "private-npm",
"version": "1.0.0",
"description": "Private npm demo",
"main": "index.js",
"scripts": {
"login": "npm config set registry https://registry.your-registry.npme.io/ && npm login --registry=https://registry.company-name.npme.io",
"login:once": "if [[ $(date +%s) -ge $AUTH_EXPIRES ]]; then npm run login && AUTH_EXPIRES=$(date -v+720M +%s); fi",
"preinstall": "npm run login:once",
"prepare": "npm run login:once"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {}
}

package.json for AWS CodeArtifact

{
"name": "private-npm",
"version": "1.0.0",
"description": "Private npm demo",
"main": "index.js",
"scripts": {
"login": "aws codeartifact login --tool npm --repository my-repo --domain my-domain",
"login:once": "if [[ $(date +%s) -ge $AUTH_EXPIRES ]]; then npm run login && AUTH_EXPIRES=$(date -v+720M +%s); fi",
"preinstall": "npm run login:once",
"prepare": "npm run login:once"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {}
}

Hope that helps your team automate and optimize your private npm registry commands!

--

--

Kim T
Creative Technology Concepts & Code

Creative Technologist, coder, music producer, and bike fanatic. I find creative uses for technology.