Skip to content

Find a component

Loading the directory…

↑ ↓ to select · Enter to open · Esc to closeBrowse all →
Contributor login

Action Cache

Expensive computation caching

Bijection source
git clone https://github.com/bijectionhq/action-cache.git
cd action-cache
git checkout 9eeb5ad93e1d86f60abc00ef8fda23a67bc370d2

Bijection repository access is required. Request access.

Description

Expensive computation caching

Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.

Available to evaluate

Review the source and its dependencies before adopting this package. Source availability does not establish npm publication or runtime qualification.

Getting started

  1. Get the source at the revision above and read its package documentation.
  2. Check the required Bijection SDK version, dependencies, and external service credentials.
  3. Follow the package setup instructions and the guide to using components.
  4. Exercise the component in your deployment according to your project’s verification rules.

Open source

Maintained source: bijectionhq/action-cache. Based on convex/action-cache. License: Apache-2.0. Consult the repository for license terms, notices, and contributor attribution.

From the upstream publisher

A Convex component for caching values that are expensive to compute.

Upstream overview

From the upstream package documentation.

Action Cache stores the results of expensive Convex actions like third-party API calls and AI operations in a persistent cache with configurable TTL expiration. The cache automatically handles cleanup through daily cron jobs and uses the action name plus arguments as the cache key. It prevents redundant API calls and reduces costs for operations like OpenAI embeddings, slow database queries, or external service requests.

Use cases

From the upstream package documentation.

  • Cache OpenAI embedding calculations for vector search to reduce API costs and latency on repeated queries
  • Store responses from slow third-party APIs like weather services, payment processors, or analytics platforms
  • Cache expensive data transformations or machine learning inference results that don't change frequently
  • Reduce redundant calls to rate-limited external services by persisting successful responses
  • Store computed results from complex business logic that involves multiple external API calls

How it works

From the upstream package documentation.

The component installs as a Convex component using app.use() in your convex.config.ts file. You create an ActionCache instance that wraps an existing internal action, specifying the action reference, an optional name for cache versioning, and TTL in milliseconds.

Calling cache.fetch(ctx, args) first checks for cached results using the action name and arguments as the key. On cache miss, it executes the wrapped action and stores the result. The force option bypasses the cache to update stale entries, useful for cron-based cache warming.

Cache management includes remove() for single entries, removeAllForName() for clearing a specific cache version, and removeAll() for complete cleanup. Expired entries are automatically deleted during retrieval and via daily background jobs.

Upstream package FAQ

How does Action Cache determine when to return cached vs fresh results?

Action Cache uses input parameters as cache keys and respects the TTL you configure. If cached data exists and hasn't expired, it returns immediately. Otherwise, it executes your action and caches the new result for subsequent calls.

Can I manually invalidate cached results before they expire?

Yes, Action Cache provides methods to manually clear specific cache entries or entire cache namespaces. This is useful when you know external data has changed and want to force fresh API calls.

What happens if my cached action throws an error?

Action Cache only stores successful results. If your action throws an error, the error propagates normally and no cache entry is created, ensuring failed operations don't prevent retries.

How much data can Action Cache store?

Action Cache uses Convex's database for storage, so it's limited by your Convex plan's database size. The component is designed for caching API responses and computed results, not large files or datasets.

Use with agents and CLI

Give your coding agent the component’s source identity and setup guidance.

agent-prompt.md
Help me integrate Action Cache with Bijection.

Read: https://bijection.com/components/action-cache/SKILL.md

Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.

Upstream README.md

View repository ↗

Documentation from convex/action-cache, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.

Sometimes your app needs to fetch information from a third-party API that is slow or costs money. Caching can help! This is a Convex component that can cache the results of expensive functions and set an optional TTL. Expired entries are cleaned up via a cron job once a day. The cache key is the ActionCache's name (defaults to function name) and the arguments to the action that generates the cache values.

import { action } from "./_generated/server";
import { components } from "./_generated/api";
import { ActionCache } from "@convex-dev/action-cache";

const cache = new ActionCache(components.actionCache, {
  action: internal.example.myExpensiveAction,
});

export const myFunction = action({
  // NOTE: If we're returning the result, we need explicitly type the return value.
  // Refer to https://docs.convex.dev/functions/actions#dealing-with-circular-type-inference for more info.
  handler: async (ctx, args): Promise<{ text: string }> => {
    // Call it with the parameters to `myExpensiveAction`
    const result = await cache.fetch(ctx, { foo: "bar" });

    // Do something with the result or just return it
    return result;
  },
});

export const myExpensiveAction = internalAction({
  args: { foo: v.string() },
  handler: async (ctx, args): Promise<{ text: string }> {
    const data = await generateLLMResponse(ctx, args);
    return data;
  }
})

To invalidate the cache, you can set a new name explicitly and/or clear cached values by name.

Pre-requisite: Convex

You'll need an existing Convex project to use the component. Convex is a hosted backend platform, including a database, serverless functions, and a ton more you can learn about here.

Run npm create convex or follow any of the quickstarts to set one up.

Installation

Install the component package:

npm install @convex-dev/action-cache

Create a convex.config.ts file in your app's convex/ folder and install the component by calling use:

// convex/convex.config.ts
import { defineApp } from "convex/server";
import cache from "@convex-dev/action-cache/convex.config.js";

const app = defineApp();
app.use(cache);

export default app;

Finally, create a new ActionCache with optional name and expiration within your Convex project, and point it to the installed component.

  • The name field can be used for identifying the function or version being used to create the values in the cache and can also be used for grouping entries to remove.
  • The ttl (Time-To-Live) field determines how long the cache entries are valid, in milliseconds.
    • If no ttl is provided, the cache entries are kept indefinitely.
    • If an ttl is provided, expired cache entries are deleted when they are retrieved and in a daily cron job.
import { ActionCache } from "@convex-dev/action-cache";
import { components } from "./_generated/api";

const cache = new ActionCache(components.actionCache, {
  action: internal.example.myExpensiveAction,
  name: "myExpensiveActionV1",
  ttl: 1000 * 60 * 60 * 24 * 7, // 7 days
});

Example

Suppose you're building an app that uses vector search. Calculating embeddings is often expensive - in our case, we are using OpenAI's API which adds latency to every search and costs money to use. We can reduce the number of API calls by caching the results!

Start by defining the Convex action that calls the API to create embeddings. Feel free to substitute your favorite embeddings API. You may need to adjust the vector dimensions in the schema in example/schema.ts accordingly.

Set your API key environment variable

npx convex env set OPENAI_API_KEY <your-api-key>
export const embed = internalAction({
  args: { text: v.string() },
  handler: async (_ctx, { text }) => {
    const apiKey = process.env.OPENAI_API_KEY;
    if (!apiKey) {
      throw new Error("OPENAI_API_KEY environment variable not set!");
    }
    const req = { input: text, model: "text-embedding-ada-002" };
    const resp = await fetch("https://api.openai.com/v1/embeddings", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${apiKey}`,
      },
      body: JSON.stringify(req),
    });
    if (!resp.ok) {
      const msg = await resp.text();
      throw new Error(`OpenAI API error: ${msg}`);
    }
    const json = await resp.json();
    const vector = json["data"][0]["embedding"];
    console.log(`Computed embedding of "${text}": ${vector.length} dimensions`);
    return vector as number[];
  },
});

Create the embeddings cache:

const embeddingsCache = new ActionCache(components.actionCache, {
  action: internal.example.embed,
  name: "embed-v1",
});

Use the cache when you run a vector search:

export const vectorSearch = action({
  args: { query: v.string(), cuisines: v.array(v.string()) },
  handler: async (ctx, args) => {
    const embedding = await embeddingsCache.fetch(ctx, {
      text: args.query,
    });
    const results = await ctx.vectorSearch("foods", "by_embedding", {
      vector: embedding,
      limit: 16,
      filter: (q) =>
        q.or(...args.cuisines.map((cuisine) => q.eq("cuisine", cuisine))),
    });
    const rows: SearchResult[] = await ctx.runQuery(
      internal.example.fetchResults,
      { results },
    );
    return rows;
  },
});

Defining multiple caches

You can use the same component for multiple actions, or multiple versions of the same action. You can specify a custom name argument to denote which cache you want to use, or change the name to start fresh, like embed-v2.

If the return value changes, it is important to change the name so you don't get unexpected values.

Updating the cache before it expires

It's convenient to lazily run the action when there is a cache miss. However, this means that some requests will be slow, and many competeing requests may race to all run the action and populate the cache.

You can avoid this by calling fetch with the force option. This will force the action to run and update the cache, even if there is a cache hit. In the meantime, other requests will get a cache hit.

If you call this from a cron job, the cache will always have an entry (unless the action fails).

Clearing values

To clear old values, you can:

  1. Remove one entry by arguments.

    await embeddingsCache.remove(ctx, { text: "target text" });
    
  2. Remove all entries for the current name (defaults to function name). This is useful if you updated the implementation and want to clear

    await embeddingsCache.removeAllForName(ctx);
    
  3. Remove all entries in the component, including all names.

    await embeddingsCache.removeAll(ctx);
    

See more example usage in example.ts.