Question

If your project config sets

registry=https://project-registry.com

and your user config sets

@foo:registry=https://user-registry.com

and you run

npm install @foo/bar

then which registry will npm search?

  1. project-registry?

  2. user-registry?

  3. first project-registry and if the package isn’t found then user-registry?

  4. something else?

Answer

  1. npm gets its config from the command line, environment variables, and four config files — project, user, global and builtin — in order. The first found value for a given key, e.g. "registry", wins.

  2. for an unscoped package, npm picks the first unscoped registry

  3. for a scoped package, npm picks

    1. the first same-scoped registry if it exists

    2. else the first unscoped registry

So: user-registry.

Remarks

When searching for any given package, npm only ever searches one registry: no fallbacks. (Contrast, say, Poetry: https://python-poetry.org/docs/repositories#package-source-constraint.)

The registry npm picks is a function of your config and the package name. As far as I know, you cannot specify a package’s source in the package.json (contrast Poetry again).

The scope is part of the package name. For example, above, npm will search for @foo/bar in user-registry. So the scope affects which registry is searched and what is searched for.

The aws codeartifact login --tool npm command, suggested in the "View connection instructions" popup in CodeArtifact, modifies the user config file. Beware!

Gotcha

Suppose

  1. Your Team has to fetch packages from private-registry

  2. but you also sometimes work in Other Team, which insists on configuring npm for its projects via user config,

  3. and packages may be published to private-registry with any scope, or no scope.

How should you configure npm in Your Team?

Insist that packages published to private-registry have one of a fixed set of scopes? Insist that Other Team uses project, not user, config? Good luck.

One approach is to set

registry=https://private-registry.com

in Your Team’s project config.

This mostly works fine: it doesn’t affect Other Team, and in Your Team’s project npm looks for scoped and unscoped packages in private-registry.

Except

  1. if someone publishes a package @myorg/cowsay to private-registry

  2. and you need it in Your Team’s project

  3. but Other Team relies on @myorg:registry=https://other-registry.com in the user config,

then when you run npm install @myorg/cowsay in Your Team’s project, npm will quietly fetch the wrong package: the one from other-registry, not private-registry.

Is there a failsafe approach?

Evidence

Experiment

% mkdir test_npm_registry_lookup && cd test_npm_registry_lookup
% npm init -y
% npm config set 'registry=https://project-registry.com' --location=project
% npm config set '@foo:registry=https://user-registry.com'
% cat .npmrc
registry=https://project-registry.com
% cat ~/.npmrc
@foo:registry=https://user-registry.com
% npm install @foo/bar
npm error network request to https://user-registry.com/@foo%2fbar failed
% # the point is that it looked up user-registry!
% # don't forget to clean up
% cd .. && rm -rf test_npm_registry_lookup && npm config delete '@foo:registry'

Docs

  • npm gets its config settings from the command line, environment variables, and npmrc files.

  • The four relevant files are:

    • per-project config file (/path/to/my/project/.npmrc)

    • per-user config file (~/.npmrc)

    • global config file ($PREFIX/etc/npmrc)

    • npm builtin config file (/path/to/npm/npmrc)

  • Each of these files is loaded, and config options are resolved in priority order.

  • Scopes can be associated with a separate registry.

  • You can also associate a scope with a registry using npm config:

npm config set @myco:registry=http://reg.example.com
  • Once a scope is associated with a registry, any npm install for a package with that scope will request packages from that registry instead.