# How to setup npm caching on GitLab

In this article, I'll show how to set up & use npm caching in GitLab CI.

# Example package

For this article, I created a project with [create-react-app](https://create-react-app.dev/). It's installing quite a few dependencies, and we need enough of them to see the positive impact of caching on the run time.

# Baseline

The baseline CI configuration is as follow:
```yml
image: node:16

stages:
  - build

build:
  stage: build
  script:
    - npm ci
```

To simplify, I only run the installation with `npm ci`. In this way, the complete time of the job is setting up the stage for the build itself - getting the `node:16` image to the agent & dependencies for npm.

I run the build with this configuration [3 times](https://gitlab.com/marcin-wosinek/npm-caching/-/jobs/1461080988), and every time the total time was **1 minute 41 seconds** in our baseline.

# Adding cache

For enabling the cache, the configuration gets a bit more complicated
```yml
image: node:16

stages:
  - build

cache:
  key: npm
  policy: pull-push
  paths:
    - .npm/

build:
  stage: build
  script:
    - npm ci --cache .npm --prefer-offline
```

Where:
* `cache:key: npm` - I want to share this cache across different CI runs. In [the documentation](https://docs.gitlab.com/ee/ci/yaml/index.html#cachekey) they show an example of how to limit cache use only to the current CI run - in the case of npm dependencies, they are safe to reuse between CI runs, especially because npm still will make sure to install them in the correct version.
* `policy: pull-push` - setting [the default cache policy](https://docs.gitlab.com/ee/ci/yaml/index.html#cachepolicy) explicitly - it will load the cache before & upload it after the job run. If we were adding more jobs, we could save few seconds making sure we only `pull-push` from the very first one, and all the others will only `pull`
* `paths: .npm/` - a folder we picked for keeping the cache. If you use git during your builds (to [create commits](https://how-to.dev/how-to-create-commit-automatically-in-a-merge-request-in-gitlab), or to run `git describe`), it would make sense to add it to your `.gitingore`
* `npm ci --cache .npm --prefer-offline` - first we tell npm where to find the cache with `--cache .npm`, second `--prefer-offline` disables online checks of cached packages.

## Cold cache

[The first run](https://gitlab.com/marcin-wosinek/npm-caching/-/jobs/1461088826) after setting up the cache took **1 minute 48 seconds**. The 7s we lost in comparison to baseline is downloading & uploading the cache files.

## Warm cache

I run the [job 3 times](https://gitlab.com/marcin-wosinek/npm-caching/-/jobs/1461106119) & always got not more than **1 minute 6 seconds**.
So we saved 35s of the installation in this simple use case - in cases of more complex apps, the time saved will get even better.

# Links
You can find the complete repository [here](https://gitlab.com/marcin-wosinek/npm-caching).

# Summary
In this article, we have seen how to speed up the npm installation in the GitLab CI. The half a minute gain we had here, in a real-world case, will most likely appear in a few places on the critical path of our CI run.
