mcpField
B
b2c-custom-caches

b2c-custom-caches

github.com/salesforcecommercecloudDevelopment5791 installsUpdated today
Repository

About b2c-custom-caches

b2c-custom-caches is a skill for Salesforce B2C Commerce developers implementing application-level caches. It explains JSON cache definitions and Script API usage, including loader-based population, scoped keys, expiration, and platform limits.

Key features

  1. 01Define caches in JSON files
  2. 02Access caches through Script API
  3. 03Populate entries with loader functions
  4. 04Scope keys by site or catalog
  5. 05Configure expiration and invalidation

Use cases

  1. 01Cache external API responses
  2. 02Avoid repeating expensive calculations
  3. 03Reuse frequently accessed product data
  4. 04Cache site-specific configuration

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/salesforcecommercecloud/b2c-developer-tooling --skill b2c-custom-caches

After installing, type/b2c-custom-cachesin Claude Code to confirm it loaded.

Bundled filesevals/trigger-evals.json

Details

From the project README

B2C Custom Caches

Custom caches improve code performance by storing data that is expensive to calculate, takes a long time to retrieve, or is accessed frequently. Caches are defined in JSON files within cartridges and accessed via the Script API.

When to Use Custom Caches

Use CaseExample
Expensive calculationsCheck if any variation product is on sale for base product display
External system responsesCache in-store availability or prices from external APIs
Configuration settingsStore configuration data from JSON files or external sources
Frequently accessed dataProduct attributes, category data, site preferences

Limitations

ConstraintValue
Total memory per app server~20 MB for all custom caches
Max caches per code version100
Max entry size128 KB
Supported value typesPrimitives, arrays, plain objects, null (not undefined)
Cross-server syncNone (caches are per-application-server)

Defining a Custom Cache

File Structure
my_cartridge/
├── package.json       # References caches.json
└── caches.json        # Cache definitions
package.json

Add a caches entry pointing to the cache definition file:

{
  "name": "my_cartridge",
  "caches": "./caches.json"
}
caches.json

Define caches with unique IDs and optional expiration:

{
  "caches": [
    {
      "id": "ProductAttributeCache"
    },
    {
      "id": "ExternalPriceCache",
      "expireAfterSeconds": 300
    },
    {
      "id": "SiteConfigCache",
      "expireAfterSeconds": 60
    }
  ]
}
PropertyRequiredDescription
idYesUnique ID across all cartridges in code version
expireAfterSecondsNoMaximum seconds an entry is retained

Using Custom Caches

Script API Classes
ClassDescription
dw.system.CacheMgrEntry point for accessing defined caches
dw.system.CacheCache instance for storing and retrieving entries
Basic Usage
var CacheMgr = require('dw/system/CacheMgr');

// Get a defined cache
var cache = CacheMgr.getCache('ProductAttributeCache');

// Get value (returns undefined if not found)
var value = cache.get('myKey');

// Store value directly
cache.put('myKey', { data: 'value' });

// Remove entry
cache.invalidate('myKey');
Recommended Pattern: get with Loader

Use get(key, loader) to automatically populate the cache on miss:

var CacheMgr = require('dw/system/CacheMgr');
var Site = require('dw/system/Site');

var cache = CacheMgr.getCache('SiteConfigCache');

// Loader function called only on cache miss
var config = cache.get(Site.current.ID + '_config', function() {
    // Expensive operation - only runs if not cached
    return loadConfigurationFromFile(Site.current);
});
Scoped Cache Keys

Include scope identifiers in keys to separate entries by context:

var CacheMgr = require('dw/system/CacheMgr');
var Site = require('dw/system/Site');

var cache = CacheMgr.getCache('ProductCache');

// Site-scoped key
var siteKey = Site.current.ID + '_' + productID;
var productData = cache.get(siteKey, loadProductData);

// Catalog-scoped key
var catalogKey = 'catalog_' + catalogID + '_' + productID;
var catalogData = cache.get(catalogKey, loadCatalogData);

// Locale-scoped key
var localeKey = request.locale + '_' + contentID;
var content = cache.get(localeKey, loadLocalizedContent);

Cache Methods

MethodDescription
get(key)Returns cached value or undefined
get(key, loader)Returns cached value or calls loader, stores result
put(key, value)Stores value directly (overwrites existing)
invalidate(key)Removes entry for key

Best Practices

Do
  • Use get(key, loader) pattern for automatic population
  • Include scope (site, catalog, locale) in cache keys
  • Set appropriate expireAfterSeconds for time-sensitive data
  • Handle cache misses gracefully (data may be evicted anytime)
  • Use descriptive cache IDs
Don't
  • Include personal user data in cache keys (keys may appear in logs)
  • Store Script API objects (only primitives and plain objects)
  • Rely on cache entries existing (no persistence guarantee)
  • Expect cross-server cache synchronization
  • Store undefined values (use null instead)

Cache Invalidation

Caches are automatically cleared when:

  • Any file in the active code version changes
  • A new code version is activated
  • Data replication completes
  • Code replication completes

Manual invalidation only affects the current application server:

var cache = CacheMgr.getCache('MyCache');

// Invalidate single entry (current app server only)
cache.invalidate('myKey');

// Storing undefined has same effect as invalidate
cache.put('myKey', undefined);

Common Patterns

Caching External API Responses
var CacheMgr = require('dw/system/CacheMgr');
var LocalServiceRegistry = require('dw/svc/LocalServiceRegistry');

var priceCache = CacheMgr.getCache('ExternalPriceCache');

function getExternalPrice(productID) {
    return priceCache.get('price_' + productID, function() {
        var service = LocalServiceRegistry.createService('PriceService', {
            createRequest: function(svc, args) {
                svc.setRequestMethod('GET');
                svc.addParam('productId', args.productID);
                return null;
            },
            parseResponse: function(svc, response) {
                return JSON.parse(response.text);
            }
        });

        var result = service.call({ productID: productID });
        return result.ok ? result.object : null;
    });
}
Caching Expensive Calculations
var CacheMgr = require('dw/system/CacheMgr');

var saleCache = CacheMgr.getCache('ProductSaleCache');

function isProductOnSale(masterProduct) {
    return saleCache.get('sale_' + masterProduct.ID, function() {
        var variants = masterProduct.variants.iterator();
        while (variants.hasNext()) {
            var variant = variants.next();
            if (isInPromotion(variant)) {
                return true;
            }
        }
        return false;
    });
}
Configuration Cache with Site Scope
var CacheMgr = require('dw/system/CacheMgr');
var Site = require('dw/system/Site');
var File = require('dw/io/File');
var FileReader = require('dw/io/FileReader');

var configCache = CacheMgr.getCache('SiteConfigCache');

function getSiteConfig() {
    var siteID = Site.current.ID;

    return configCache.get(siteID + '_config', function() {
        var configFile = new File(File.IMPEX + '/src/config/' + siteID + '.json');
        if (!configFile.exists()) {
            return null;
        }

        var reader = new FileReader(configFile);
        var content = reader.getString();
        reader.close();

        return JSON.parse(content);
    });
}

Troubleshooting

IssueCauseSolution
Cache not found exceptionCache ID not defined in any caches.jsonAdd cache definition to caches.json
Duplicate cache ID errorSame ID used in multiple cartridgesUse unique IDs across all cartridges
Entry not storedValue exceeds 128 KB limitReduce data size or cache subsets
Entry not storedValue contains Script API objectsUse only primitives and plain objects
Unexpected cache missesDifferent app server or cache clearedAlways handle misses gracefully

Check the custom error log and custom warn log for cache-related messages.

Comments

Markdown supported