Fine-tuning Caching with Inputs

When Nx computes the hash for a given operation, it takes into account the inputs of the target. The inputs are a list of file sets, runtime inputs and environment variables that affect the output of the target. If any of the inputs change, the cache is invalidated and the target is re-run.

To understand some of the examples below, let's imagine the following simple workspace.


Global vs per-project inputs

Tasks can have inputs defined for them globally in the nx.json file or on a per-project basis in the project configuration.

1{ 2 "targetDefaults": { 3 "build": { 4 "inputs": ["..."] 5 } 6 } 7} 8

Include all project files and dependencies in cache hash

The following definition includes all files of the project itself as well as all of its dependencies in the cache hash, hence telling Nx to invalidate the cache whenever

  • any file of the project itself changes
  • any file of any dependency changes
1{ 2 "namedInputs": { 3 "default": ["{projectRoot}/**/*"] 4 }, 5 "targetDefaults": { 6 "build": { 7 "inputs": ["default", "^default"] 8 } 9 } 10} 11

This definition is the default behavior of Nx, even if you don't specify any inputs at all. This is a rather cautious approach. It will always give you correct output, but it might re-run a task in some cases where the cache could have been used instead.

Note, Nx uses the minimatch library to process glob patterns.


Note how you can use {projectRoot} and {workspaceRoot} as placeholders to simplify writing glob definitions.

If you're wondering what namedInputs are, read the next section.

Reusing inputs definitions with namedInputs

If you find yourself reusing the same inputs definitions, you can instead create a namedInput. It is like a variable definition which can then be reused in the inputs array.

1{ 2 "namedInputs": { 3 "default": ["{projectRoot}/**/*"] 4 }, 5 "targetDefaults": { 6 "build": { 7 "inputs": ["default", "^default"] 8 } 9 } 10} 11

The ^ character at the beginning of the ^default string means this entry applies to the project dependencies of the project, not the project itself. In our example myreactapp depends on shared-ui, so if we run

nx build myreactapp

...then because we defined

  • "inputs": ["default",...] - it will invalidate the cache of myreactapp whenever some src file of myreactapp itself changes
  • "inputs": [..., "^default"] - it will in addition invalidate the cache of myreactapp whenever a file of shared-ui (or any of its dependencies) changes

Exclude files from invalidating cache

Sometimes you might want to exclude specific project files so that they don't invalidate the cache for a given target. For example, we want spec/test files to invalidate the test target (by explicitly including *.spec.ts files), but we might want to optimize and exclude them from our build target. Hence, whenever we just change a test file, our build cache would still be working.

Here's how we could define that, starting from our default situation:

1{ 2 "namedInputs": { 3 "default": ["{projectRoot}/**/*"], 4 "production": ["default", "!{projectRoot}/**/?(*.)+spec.ts"] 5 }, 6 "targetDefaults": { 7 "build": { 8 "inputs": ["production", "^production"] 9 } 10 } 11} 12

Note how we define a new production named input which uses the default named input and then excludes (note the !) all spec related files. As a result, if a spec file changes either in myreactapp or in shared-ui, the build target cache will not be invalidated.

Include environment variables in cache hash

You can also include environment variables in the cache hash key calculation in order to invalidate the cache if the environment variable value changes.

1{ 2 "namedInputs": { 3 "default": ["{projectRoot}/**/*"] 4 }, 5 "targetDefaults": { 6 "build": { 7 "inputs": [ 8 "default", 9 "^default", 10 11 // this will include the value of API_KEY in the cache hash 12 { "env": "API_KEY" } 13 ] 14 } 15 } 16} 17

Include Runtime Information in the Cache Hash

Sometimes you might also want to consider runtime information as part of your hash. Assume you have a deploy target for the myreactapp and assume we use Fly for it. We might want to make sure to just use the cache if the Fly version matches.

You can define it globally in the nx.json but these targets are usually defined right where the project is, so here's an example of a package.json and project.json definition.

1{ 2 "name": "myreactapp", 3 "dependencies": {}, 4 "scripts": { 5 "deploy": "fly deploy" 6 }, 7 ... 8 "nx": { 9 "targets": { 10 "deploy": { 11 "inputs": [ 12 ... 13 14 // includes the value of running "fly version" in the cache hash 15 { "runtime": "fly version" } 16 ], 17 "dependsOn": ["build"], 18 } 19 } 20 } 21} 22

Include or Exclude External NPM dependencies from the Cache

You can also get more fine-grained when it comes to external dependencies. In our example of the deployment, we don't really rely on any NPM packages for the deployment step, so we could ignore all of them. This can be done by using the externalDependencies property.

1{ 2 "name": "myreactapp", 3 "dependencies": {}, 4 "scripts": { 5 "deploy": "fly deploy" 6 }, 7 ... 8 "nx": { 9 "targets": { 10 "deploy": { 11 "inputs": [ 12 ..., 13 // this explicitly tells the hasher to ignore all external packages when calculating the hash 14 { "externalDependencies": [] }, 15 16 // includes the value of running "fly version" in the cache hash 17 { "runtime": "fly version" } 18 ], 19 "dependsOn": ["build"], 20 } 21 } 22 } 23} 24

By explicitly providing an empty array, we ignore all changes to external NPM packages. Similarly we could have another example, where we depend on just one specific NPM package. Like if we use Lerna for publishing, we can define it like this:

1{ 2 "targets": { 3 "publish": { 4 "command": "lerna publish", 5 "inputs": [ 6 "production", 7 // we explicitly say that our run-commands depends on lerna only 8 { "externalDependencies": ["lerna"] } 9 ], 10 "dependsOn": ["build"], 11 "cwd": "dist/{projectRoot}" 12 } 13 } 14} 15