[miniflare] Add purgeCache() method to clear cache entries programmatically#12462
Merged
petebacondarwin merged 2 commits intomainfrom Feb 17, 2026
Merged
[miniflare] Add purgeCache() method to clear cache entries programmatically#12462petebacondarwin merged 2 commits intomainfrom
purgeCache() method to clear cache entries programmatically#12462petebacondarwin merged 2 commits intomainfrom
Conversation
🦋 Changeset detectedLatest commit: 1f01714 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
|
Claude finished @petebacondarwin's task —— View job Changeset ReviewReviewing changeset file(s) for this PR... Tasks:
✅ All changesets look good The changeset
|
create-cloudflare
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-pool-workers
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
4 tasks
fb74c4b to
27575b9
Compare
1cbcf63 to
eb933ad
Compare
…ammatically Add Miniflare#purgeCache() method that allows developers to clear cached data during local development without restarting the Miniflare instance. This is useful when working with Workers Sites or any application that uses the Cache API. The method: - Purges the default cache when called without arguments - Purges a specific named cache when called with a cache name - Returns the number of entries deleted Implementation includes: - KeyValueStorage#deleteAll() method to delete all entries - CacheObject#purgeAll route handler for DELETE /purge-all - Tests for default cache, named cache, and empty cache scenarios Fixes #4387
…ve cf property The previous implementation called the CacheObject Durable Object directly from Node.js, but the cf property was silently dropped by Node.js's Request constructor in the proxy system. This caused MiniflareDurableObject#fetch() to crash when asserting cf.miniflare.name. This fix routes purgeCache() requests through the cache-entry worker service instead, which runs inside workerd and properly sets cf.miniflare.name before forwarding to the CacheObject DO. This follows the same path that normal Cache API operations use. Changes: - Add SERVICE_CACHE constant to CoreBindings - Add cache service binding to ProxyServer's env - Rewrite purgeCache() to use the cache service Fetcher
eb933ad to
1f01714
Compare
dario-piotrowicz
approved these changes
Feb 17, 2026
Contributor
Author
|
Tested manually with a Worker: // Simple Cloudflare Worker that uses the Cache API
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const cache = caches.default;
// PUT an item in the cache
if (url.pathname === "/cache-put") {
const key = url.searchParams.get("key");
const value = url.searchParams.get("value");
if (!key || !value) {
return new Response("Missing key or value query param", { status: 400 });
}
// Create a cache key URL and response to store
const cacheKey = new Request(`http://cache/${key}`);
const cacheResponse = new Response(value, {
headers: { "Cache-Control": "max-age=3600" },
});
await cache.put(cacheKey, cacheResponse);
return new Response(`Cached "${key}" = "${value}"`);
}
// GET an item from the cache
if (url.pathname === "/cache-get") {
const key = url.searchParams.get("key");
if (!key) {
return new Response("Missing key query param", { status: 400 });
}
const cacheKey = new Request(`http://cache/${key}`);
const cachedResponse = await cache.match(cacheKey);
if (!cachedResponse) {
return new Response(`Key "${key}" not found in cache`, { status: 404 });
}
const value = await cachedResponse.text();
return new Response(`Found "${key}" = "${value}"`);
}
// Default response
return new Response("Hello from Miniflare Cache Test Worker!");
},
};and the following test code: import { Miniflare } from "miniflare";
async function main() {
console.log("Creating Miniflare instance...\n");
const mf = new Miniflare({
modules: true,
scriptPath: "./src/index.js",
cache: true, // Enable cache (this is the default)
});
try {
// Test 1: Add items to the cache
console.log("=== Step 1: Adding items to cache ===");
const put1 = await mf.dispatchFetch(
"http://localhost/cache-put?key=greeting&value=hello"
);
console.log("PUT greeting:", await put1.text());
const put2 = await mf.dispatchFetch(
"http://localhost/cache-put?key=name&value=world"
);
console.log("PUT name:", await put2.text());
const put3 = await mf.dispatchFetch(
"http://localhost/cache-put?key=number&value=42"
);
console.log("PUT number:", await put3.text());
// Test 2: Verify items are in the cache
console.log("\n=== Step 2: Verifying items are cached ===");
const get1 = await mf.dispatchFetch(
"http://localhost/cache-get?key=greeting"
);
console.log("GET greeting:", await get1.text(), `(status: ${get1.status})`);
const get2 = await mf.dispatchFetch("http://localhost/cache-get?key=name");
console.log("GET name:", await get2.text(), `(status: ${get2.status})`);
const get3 = await mf.dispatchFetch(
"http://localhost/cache-get?key=number"
);
console.log("GET number:", await get3.text(), `(status: ${get3.status})`);
// Test 3: Purge the cache
console.log("\n=== Step 3: Calling mf.purgeCache() ===");
const purgedCount = await mf.purgeCache();
console.log(`purgeCache() returned: ${purgedCount} entries purged`);
// Test 4: Verify cache is now empty
console.log("\n=== Step 4: Verifying cache is empty after purge ===");
const getAfter1 = await mf.dispatchFetch(
"http://localhost/cache-get?key=greeting"
);
console.log(
"GET greeting after purge:",
await getAfter1.text(),
`(status: ${getAfter1.status})`
);
const getAfter2 = await mf.dispatchFetch(
"http://localhost/cache-get?key=name"
);
console.log(
"GET name after purge:",
await getAfter2.text(),
`(status: ${getAfter2.status})`
);
const getAfter3 = await mf.dispatchFetch(
"http://localhost/cache-get?key=number"
);
console.log(
"GET number after purge:",
await getAfter3.text(),
`(status: ${getAfter3.status})`
);
// Summary
console.log("\n=== Summary ===");
if (
getAfter1.status === 404 &&
getAfter2.status === 404 &&
getAfter3.status === 404
) {
console.log("SUCCESS: All cache entries were purged!");
} else {
console.log("FAILURE: Some cache entries still exist after purge");
}
} finally {
// Clean up
console.log("\nDisposing Miniflare instance...");
await mf.dispose();
}
}
main().catch((err) => {
console.error("Test failed:", err);
process.exit(1);
});Resulting in... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4387.
Add
Miniflare#purgeCache()method that allows developers to clear cached data during local development without restarting the Miniflare instance. This is useful when working with Workers Sites or any application that uses the Cache API.Changes
deleteAll()method to delete all entries and garbage collect associated blobs@DELETE("/purge-all")route handlerpurgeCache(cacheName?: string)method that returns the number of purged entriesSERVICE_CACHEconstant for the cache service bindingpurgeCache()to route through the cache-entry workerImplementation Details
The
purgeCache()method routes requests through the cache-entry worker service instead of calling the CacheObject Durable Object directly. This is necessary because:Requestconstructor silently drops the non-standardcfpropertyfetcherFetchCallcreates a newRequestin Node.js before forwardingMiniflareDurableObject#fetch()requirescf.miniflare.nameand will crash if it's undefinedBy routing through the cache-entry worker (which runs inside workerd), the
cf.miniflare.nameproperty is properly set before forwarding to the CacheObject DO. This follows the same path that normal Cache API operations use.Usage
purgeCachedocs to Miniflare cache page cloudflare-docs#28172