332

I'm working with continuous integration and discovered the npm ci command.

I can't figure what the advantages are of using this command for my workflow.

Is it faster? Does it make the test harder, okay, and after?

RobC
  • 16,905
  • 14
  • 51
  • 62
Webwoman
  • 5,914
  • 7
  • 26
  • 67

6 Answers6

493

From the npm docs:

In short, the main differences between using npm install and npm ci are:

  • The project must have an existing package-lock.json or npm-shrinkwrap.json.
  • If dependencies in the package lock do not match those in package.json, npm ci will exit with an error, instead of updating the package lock.
  • npm ci can only install entire projects at a time: individual dependencies cannot be added with this command.
  • If a node_modules is already present, it will be automatically removed before npm ci begins its install.
  • It will never write to package.json or any of the package-locks: installs are essentially frozen.

Essentially, npm install reads package.json to create a list of dependencies and uses package-lock.json to inform which versions of these dependencies to install. If a dependency is not in package-lock.json it will be added by npm install.

npm ci (named after Continuous Integration) installs dependencies directly from package-lock.json and uses package.json only to validate that there are no mismatched versions. If any dependencies are missing or have incompatible versions, it will throw an error.

Use npm install to add new dependencies, and to update dependencies on a project. Usually, you would use it during development after pulling changes that update the list of dependencies but it may be a good idea to use npm ci in this case.

Use npm ci if you need a deterministic, repeatable build. For example during continuous integration, automated jobs, etc. and when installing dependencies for the first time, instead of npm install.

npm install

  • Installs a package and all its dependencies.
  • Dependencies are driven by npm-shrinkwrap.json and package-lock.json (in that order).
  • without arguments: installs dependencies of a local module.
  • Can install global packages.
  • Will install any missing dependencies in node_modules.
  • It may write to package.json or package-lock.json.
    • When used with an argument (npm i packagename) it may write to package.json to add or update the dependency.
    • when used without arguments, (npm i) it may write to package-lock.json to lock down the version of some dependencies if they are not already in this file.

npm ci

  • Requires at least npm v5.7.1.
  • Requires package-lock.json or npm-shrinkwrap.json to be present.
  • Throws an error if dependencies from these two files don't match package.json.
  • Removes node_modules and install all dependencies at once.
  • It never writes to package.json or package-lock.json.

Algorithm

While npm ci generates the entire dependency tree from package-lock.json or npm-shrinkwrap.json, npm install updates the contents of node_modules using the following algorithm (source):

load the existing node_modules tree from disk
clone the tree
fetch the package.json and assorted metadata and add it to the clone
walk the clone and add any missing dependencies
  dependencies will be added as close to the top as is possible
  without breaking any other modules
compare the original tree with the cloned tree and make a list of
actions to take to convert one to the other
execute all of the actions, deepest first
  kinds of actions are install, update, remove and move
Community
  • 1
  • 1
lucascaro
  • 10,640
  • 2
  • 31
  • 40
  • 2
    I didn't know `npm install` could write to package.json. Do you know what it could write in here? – Veve Nov 15 '18 at 17:54
  • 6
    well that might be a bit misleading... it will write to package.json when you use it to install, update, or remove dependencies. I'll make that more clear in the text, thanks! – lucascaro Nov 15 '18 at 17:56
  • 6
    `npm install package` could modify both `package-lock.json` **and** `package.json`, while `npm install` whithout arguments would only modify `package-lock.json` – knobo Aug 08 '19 at 10:27
  • Recently, I have found that if we want npm not to install missing dependencies from `package-lock.json`, we can rewrite `"require": true"` in `package-lock.json`. – PuiMan Cheui Jan 21 '20 at 09:26
  • How the cache works? Will it be slower or faster if compare with old way by `npm install`? – BMW Jan 22 '20 at 01:55
  • I believe that one of the differences is also that `npm install` installs `devDependencies` while `npm ci` does not, as far as I understand. – Link14 Jan 23 '20 at 20:01
  • 1
    @Link14 installation of `devDependencies` is controlled by the `--production` flag or the `NODE_ENV` environment variable, for both `npm i` and `npm ci` – lucascaro Feb 17 '20 at 03:15
  • While not explicitly said anywhere in the docs, the `ci` in `npm ci` is better understood as clean install and not continuous integration. – William Denman Mar 05 '21 at 18:31
  • The `in short` is not short.. – user218867 May 10 '21 at 09:51
29

npm ci will delete any existing node_modules folder and relies on the package-lock.json file to install the specific version of each package. It is significantly faster than npm install because it skips some features. It's clean state install is great for ci/cd pipelines and docker builds! You also use it to install everything all at once and not specific packages.

BMW
  • 34,279
  • 9
  • 81
  • 95
James Harrison
  • 291
  • 3
  • 4
10

The documentation you linked had the summary:

In short, the main differences between using npm install and npm ci are:

  • The project must have an existing package-lock.json or npm-shrinkwrap.json.
  • If dependencies in the package lock do not match those in package.json, npm ci will exit with an error, instead of updating the package lock.
  • npm ci can only install entire projects at a time: individual dependencies cannot be added with this command.
  • If a node_modules is already present, it will be automatically removed before npm ci begins its install.
  • It will never write to package.json or any of the package-locks: installs are essentially frozen.
OscarRyz
  • 184,433
  • 106
  • 369
  • 548
4

While everyone else has answered the technical differences none explain in what situations to use both.

You should use them in different situations.

npm install is great for development and in the CI when you want to cache the node_modules directory. When to use this? You can do this if you are making a package for other people to use (you do NOT include node_modules in such a release). Regarding the caching, be careful, if you plan to support different versions of Node.js remember that node_modules might have to be reinstalled due to differences between the Node.js runtime requirements. If you wish to stick to one version, stick to the latest LTS.

npm ci should be used when you are to test and release a production application (a final product, not to be used by other packages) since it is important that you have the installation be as deterministic as possible, this install will take longer but will ultimately make your application more reliable (you do include node_modules in such a release). Stick with LTS version of Node.js.

Bonus: You could mix them depending on how complex you want to make it. On feature branches in git you could cache the node_modules to increase your teams productivity and on the merge request and master branches rely on npm ci for a deterministic outcome.

basickarl
  • 25,903
  • 43
  • 172
  • 270
  • I don't think that there is any scenario where `npm i` should be used over `npm ci` except when you want to update your dependencies. `npm ci` is always better because deterministic behaviour is always better – enanone Mar 20 '21 at 10:43
  • @enanone As I stated `npm i` caches as it is quicker, `npm ci` is slower since it does a full reinstall. They are both useful. – basickarl Mar 23 '21 at 09:15
  • 1
    `npm ci` is just as fast if every package is in the npm cache – enanone Mar 23 '21 at 11:04
  • 1
    In my case, `npm ci` is significantly slower even when done repeatedly, with a local NPM cache: `npm install` is about 2s, `npm ci` 16s when run for the same project. After a cache clear they're the same. We desperately need something that would install from package-lock but don't start by deleting node_modules: https://github.com/npm/cli/issues/564 – Piedone Apr 13 '21 at 21:11
3

The commands are very similar in functionality however the difference is in the approach taken to install the dependencies specified in your package.json and package-lock.json files.

npm ci performs a clean install of all the dependencies of your app whereas npm install may skip some installations if they already exist on the system. A problem may arise if the version already installed on the system isn't the one your package.json intended to install i.e. the installed version is different from the 'required' version.

Other differences would be that npm ci never touches your package*.json files. It will stop installation and show an error if the dependency versions do not match in the package.json and package-lock.json files.

You can read a much better explanation from the official docs here.

Additionally, you may want to read about package locks here.

krishnakeshan
  • 773
  • 1
  • 8
  • 17
2

It is worth having in mind that light node docker images like alpine do not have Python installed which is a dependency of node-gyp which is used by npm ci.

I think it's a bit opinionated that in order to have npm ci working you need to install Python as dependency in your build.

More info here Docker and npm - gyp ERR! not ok

teseo
  • 106
  • 5