Search code examples
node.jsnpmnpm-registry

npmrc multiple registries for the same scope


Is it possible to contain multiple registries for the same scope? In my company, we use @mycompany scope for both public NPM registry as well as internal registries.

I tried to do

@mycompany:registry=https://company.registry.com
@mycompany:registry=https://registry.npmjs.org

but this did not work.


Solution

  • No, you cannot. Unfortunately npm can handle only one scope peer registry. But you might use a proxy as Verdaccio for that that handle the distribution for you. https://verdaccio.org/docs/en/uplinks

    I will mock an example of configuration using Verdaccio:

    I mocked a "company registry" with no external access (http://localhost:5000/).

    storage: /Users/test/.local/share/verdaccio/storage_company_registry
    
    auth:
      htpasswd:
        file: ./htpasswd   
    packages:
      '@*/*':
        access: $all
        publish: $authenticated
    
      '**':
        access: $all
        publish: $all
    middlewares:
      audit:
        enabled: true
    logs:
      - {type: stdout, format: pretty, level: http}
    

    As you can see, no remotes (uplink) configured, it's completely offline.

    Then I'll run my proposal, Verdaccio (http://localhost:4873/) that ask first to the company registry and if the package is not found there, it will fetch from the public registry (npmjs).

    storage: /Users/test/.local/share/verdaccio/storage_proxy
    
    auth:
      htpasswd:
        file: ./htpasswd
    uplinks:
      npmjs:
        url: https://registry.npmjs.org/
      company:
        url: https://company.registry.com
    
    packages:
      '@company/*':
        access: $all
        publish: $authenticated
        proxy: company npmjs  
      '**':
        access: $all
        publish: $authenticated
        proxy: npmjs
    middlewares:
      audit:
        enabled: true
    logs:
      - {type: stdout, format: pretty, level: http}
    

    As a PoC (I am switching @company by @babel) I run npm install @babel/types --registry http://localhost:4873/. The result is the following

     warn --- config file  - /Users/test/.config/verdaccio/config.yaml
     warn --- Plugin successfully loaded: htpasswd
     warn --- Plugin successfully loaded: audit
     warn --- http address - http://localhost:4873/ - verdaccio/4.0.0-alpha.4
     http --> 404, req: 'GET http://localhost:5000/@babel%2Ftypes' (streaming)
     http --> 404, req: 'GET http://localhost:5000/@babel%2Ftypes', bytes: 0/43
     http --> 200, req: 'GET https://registry.npmjs.org/@babel%2Ftypes' (streaming)
     http --> 200, req: 'GET https://registry.npmjs.org/@babel%2Ftypes', bytes: 0/93375
     http <-- 200, user: null(127.0.0.1), req: 'GET /@babel%2ftypes', bytes: 0/22072
     http --> 200, req: 'GET https://registry.npmjs.org/esutils' (streaming)
     http --> 200, req: 'GET https://registry.npmjs.org/esutils', bytes: 0/23169
     http <-- 200, user: null(127.0.0.1), req: 'GET /esutils', bytes: 0/3854
     http --> 200, req: 'GET https://registry.npmjs.org/to-fast-properties' (streaming)
     http --> 200, req: 'GET https://registry.npmjs.org/to-fast-properties', bytes: 0/8239
     http <-- 200, user: null(127.0.0.1), req: 'GET /to-fast-properties', bytes: 0/1725
     http --> 200, req: 'GET https://registry.npmjs.org/lodash' (streaming)
     http --> 200, req: 'GET https://registry.npmjs.org/lodash', bytes: 0/185410
     http <-- 200, user: null(127.0.0.1), req: 'GET /lodash', bytes: 0/14307
     http <-- 200, user: null(127.0.0.1), req: 'POST /-/npm/v1/security/audits/quick', bytes: 474/146
    

    Your node package manager will speak with (http://localhost:4873/) and Verdaccio will try fo fetch package from the internal registry, the result is 404, in the second iteration will fetch the package from npmjs, resulting in a 200.

    Having a proxy registry makes the process more transparent for your team, all is centralized and more efficient, ihmo.

    I hope that helps.