arrow_back All posts
Caching and busting front-end task outputs

Caching and busting front-end task outputs

How to prevent redundant compilations on the front-end? Let's figure out how to speed things up by storing and retrieving front-end task output cache artifacts using the Nx build system.

Introduction

Nx is a build system containing many tools and functions to help optimize the development process of front-end applications. One of the powerful features Nx offers is task caching - caching the outputs of tasks and reusing them when the same task is run again.

In this post, we'll try to figure out how to cache your application task outputs using the Nx build system.

How does caching works?

A visualization of the difference between "inputs" and "outputs" in Nx build systemA visualization of the difference between "inputs" and "outputs" in Nx build system

The Nx caching mechanism is based on the idea of having a split between two main concepts:

  • Inputs — the files, configurations, and environment variables that a task depends on. Inputs could include source code files, configuration files, and environment settings.
  • Outputs — the artifacts produced by running a task. Outputs could be the generated static files, the /public directory, or other build artifacts.

Nx caches task outputs by creating a hash of the inputs. If the inputs did not change, Nx will skip the task execution and retrieve the existing cached output instead.

When you enable caching for your application using Nx, you can enjoy having:

  • Reduced build times — by reusing cached outputs, Nx can significantly cut down on build times, speeding up your development workflow.

  • Resource efficiency — by reducing redundant task executions, Nx can save computational resources and lower infrastructure costs of CI/CD pipelines.

  • Improved developer experience — by having faster builds, you experience less waiting and a more efficient development setup.

How to configure caching?

Let's use a simple example of building a Next.js application with caching enabled for build, lint, and test tasks.

👉 Create a workspace

First, you need to set up an Nx workspace by running:

$ npx create-nx-workspace@latest
👉 Enable caching of tasks

To enable caching, you need to specify the cacheableOperations in your nx.json configuration. Here’s a basic configuration:

// nx.json

{
  "tasksRunnerOptions": {
    "default": {
      "runner": "@nrwl/workspace/tasks-runners/default",
      "options": {
        "cacheableOperations": ["build", "lint", "test"]
      }
    }
  }
}
👉 Configure targets and outputs

For caching to work correctly in your application, you need to specify which tasks should have their outputs cached. For a Next.js project, you might cache build, lint, and test operations:

// package.json

{
  "nx": {
    "namedInputs": {
      "default": ["{projectRoot}/**/*"]
    },
    "targets": {
      "lint": {
        "outputs": [],
        "dependsOn": ["^build"]
      },
      "build": {
        "outputs": ["{workspaceRoot}/public"],
        "dependsOn": ["^build"]
      },
      "test": {
        "outputs": [],
        "dependsOn": ["build"]
      }
    }
  }
}
👉 Done!

Once configured, running a command like nx build your-application will produce an output that will end up in the Nx cache storage. On the next run, Nx will use the latest cache if the inputs haven’t changed.

Invalidating the cache

A visualisation of cache invalidation using the Nx build systemA visualisation of cache invalidation using the Nx build system

When dealing with cache, we also have to keep in mind cache invalidation. By default, Nx handles this by providing a default mechanism based on hashing the inputs. Cache invalidation is complicated and it might be tricky to ensure the cache reliably reflects the current state of your project:

  • Configuration changes — if you changed any configurations (like .babelrc or next.config.js), you need to make sure the cache is cleared correctly.
  • Dependency updates — if you update any dependencies in your project, this will most probably affect the output artifacts. Make sure you clear the cache if you make significant dependency updates.
  • Environment changes — if your build uses environment variables, changes to them should invalidate the cache.

To manually clear the cache, you can run:

$ nx reset

What's next?

A visualization of caching multiple application outputs in a monorepo using the Nx build systemA visualization of caching multiple application outputs in a monorepo using the Nx build system

Now that you have your basic caching set up using Nx, you might want to consider these enhancements:

  1. Enabling distributed caching — you can use Nx Cloud to share cache across different machines. This can help a lot with optimizing the CI/CD workflows.
  2. Adding a custom configuration — you can provide custom configurations and specify which tasks and files should be included or excluded from caching for more granular control.
  3. Searching for sneaky issues — you can use Nx Cloud’s analytics to monitor cache hits/misses to optimize further.
  4. Automating cache management — you can implement scripts to automate cache clearing based on specific triggers. This will help ensure your cache is always up to date.
NxNextJS