[Support Guide] Using private NPM modules on Netlify

Took a lot of digging but eventually came across this issue which pointed me to this line which shows that

If you only need to set an auth token to access private NPM modules, you only need to set the NPM_TOKEN env variable.

You do not need to create a .npmrc file in your repo as the npm build process will detect the env var and do it for you. This will not help you if you also need to use a different registry, but my guess is most folks just need the token set.

I hope the netlify team will update the instructions and point this out! It would save a lot of folks some hassle.

1 Like

@theo, that’s great sleuthing! We’ll get this Support Guide updated to reflect that. Thank you!!

I tried using @rvanmil’s preinstall approach with yarn 1.22.4 and Github Package Registry but it did not work for me. Later I found this https://github.com/yarnpkg/yarn/issues/8015 which indicated that the token must also be passed in an authorization header. I’ve modified his script to the following and it appears to work:

// Netlify does not support Github Packages (or other private package registries besides npm), options are:
//   - Commit .npmrc to repo - However, now we have a secret token inside our repo
//   - Environment variable in .npmrc - However, this requires all developer machines to have the same environment variable configured
//   - Get creative with the preinstall script... :)

const fs = require('fs');
const { spawnSync } = require('child_process');

const scope = 'your_scope';
const authToken = process.env.GITHUB_TOKEN;

// Only run this script on Netlify, this is a default Netlify environment variable
if (process.env.NETLIFY === 'true') {
  // Check if this has already run (we spawn yarn again at the end)
  if (process.env.NETLIFY_NPMRC_DONE === 'true') {
    return;
  }

  // Check if .npmrc already exists, if it does then do nothing (otherwise we create an infinite yarn loop)
  if (fs.existsSync('.npmrc')) {
    console.warn('Skipping .npmrc write because it already exists');
    return;
  }

  console.log(`Creating .npmrc with auth tokens for @${scope}, provided by GITHUB_TOKEN environment variable...`);

  // Create .npmrc
  fs.writeFileSync(
    '.npmrc',
    `//npm.pkg.github.com/:_authToken=${authToken}\n//npm.pkg.github.com/:_header:Authorization=token ${authToken}\n@${scope}:registry=https://npm.pkg.github.com\n`,
  );

  // Is this necessary?
  fs.chmodSync('.npmrc', 0o600);

  // Run yarn again, because the yarn process which is executing
  // this script won't pick up the .npmrc file we just created.
  // The original yarn process will continue after this second yarn process finishes,
  // and when it does it will report "success Already up-to-date."
  console.log(`.npmrc created. Starting yarn install...`);
  spawnSync('yarn', { stdio: 'inherit', env: { ...process.env, NETLIFY_NPMRC_DONE: true } });
}

I use neither .yarnrc nor .npmrc in the project.

Hiya @bdefore and welcome to our community! Thanks so much for that great workflow suggestion and for sharing your code. Just to clarify, could you confirm if you do have things working acceptably (I understand it is rather indirect, but you’re not blocked, right?)

Yes, this is working sufficiently for me, although I do wish it weren’t necessary and there was a more integrated solution through toml configuration. This approach only satisfies because I don’t otherwise use .npmrc for configuration.

One addition I’ve made since posting this is to check for YARN_VERSION and log out that it has no effect if using yarn 2 or higher - this version of yarn no longer infers configuration from .npmrc.