{"id":21207190,"url":"https://github.com/hansottowirtz/cloudflare-workers-cache","last_synced_at":"2025-07-10T08:33:57.340Z","repository":{"id":233171647,"uuid":"786213671","full_name":"hansottowirtz/cloudflare-workers-cache","owner":"hansottowirtz","description":"`unstable_cache` from Next.js for Cloudflare Workers","archived":false,"fork":false,"pushed_at":"2024-04-20T18:12:50.000Z","size":1072,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-28T03:29:48.756Z","etag":null,"topics":["cache","cloudflare-cache","cloudflare-workers","kv-store","nextjs"],"latest_commit_sha":null,"homepage":"https://cloudflare-workers-cache-example-app.hansottowirtz.be/sum?a=1\u0026b=1","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hansottowirtz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-13T19:01:03.000Z","updated_at":"2024-05-28T10:49:01.928Z","dependencies_parsed_at":"2024-04-14T09:55:54.598Z","dependency_job_id":"d58d2757-8935-4a8e-8853-5ef072c1437a","html_url":"https://github.com/hansottowirtz/cloudflare-workers-cache","commit_stats":null,"previous_names":["hansottowirtz/cloudflare-workers-cache"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hansottowirtz%2Fcloudflare-workers-cache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hansottowirtz%2Fcloudflare-workers-cache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hansottowirtz%2Fcloudflare-workers-cache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hansottowirtz%2Fcloudflare-workers-cache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hansottowirtz","download_url":"https://codeload.github.com/hansottowirtz/cloudflare-workers-cache/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225629832,"owners_count":17499295,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","cloudflare-cache","cloudflare-workers","kv-store","nextjs"],"created_at":"2024-11-20T20:58:03.835Z","updated_at":"2024-11-20T20:58:04.404Z","avatar_url":"https://github.com/hansottowirtz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `unstable_cache` from Next.js for Cloudflare Workers\n\nCache any JSON-serializable result from an async function. It uses the same API as [`unstable_cache` from Next.js](https://nextjs.org/docs/app/api-reference/functions/unstable_cache).\n\n- Simple wrapper function over the [Cloudflare Workers Cache API](https://developers.cloudflare.com/workers/runtime-apis/cache/#accessing-cache).\n- Supports time-based and tag-based cache revalidation.\n- Stores tag revalidation data in a KV store.\n- No need for a Cloudflare Enterprise account for tag revalidation.\n\n```ts\nconst getUser = (id: number) =\u003e sql`SELECT * FROM users WHERE id = ${id}`;\n\nconst cachedGetUser = cache(getUser, [\"get-user\"], {\n  tags: [\"users\"],\n  revalidate: 60,\n});\n\n// on edit\nrevalidateTag(\"users\");\n```\n\n## Installation\n\n```bash\nnpm install cloudflare-workers-cache\n```\n\n## Setup\n\nFirst, create a kv namespace:\n\n```bash\nwrangler kv:namespace create CACHE_TAG_STORE\n```\n\nThen, adjust your `wrangler.toml`:\n\n```toml\ncompatibility_flags = [\"nodejs_compat\"] # needed because this library uses AsyncLocalStorage\n\nkv_namespaces = [{ binding = \"CACHE_TAG_STORE\", id = \"...\" }] # copy this from the output of the previous command\n```\n\n## Usage\n\nSee [example-app/src/index.ts](example-app/src/index.ts) for a fully working example.\n\n```ts\nimport {\n  cache,\n  revalidateTag,\n  provideCacheToFetch,\n  createCfCacheObjectCache,\n  createCfKvCacheTagStore,\n} from \"cloudflare-workers-cache\";\n\n// cache an async function\nconst cachedSum = cache(sum, [\"sum\"], {\n  tags: [\"sum\"],\n});\n\n// configure which object cache and cache tag store to use\nconst cacheConfig = {\n  objectCache: createCfCacheObjectCache(caches.open(\"cache\")),\n  cacheTagStore: (env) =\u003e createCfKvCacheTagStore(env.CACHE_TAG_STORE),\n};\n\nexport default {\n  fetch: provideCacheToFetch(cacheConfig, async (req) =\u003e {\n    const url = new URL(req.url);\n    if (url.pathname === \"/sum\") {\n      const a = +url.searchParams.get(\"a\")!;\n      const b = +url.searchParams.get(\"b\")!;\n      const result = await cachedSum(a, b);\n      return new Response(result.toString());\n    } else if (url.pathname === \"/revalidate\") {\n      revalidateTag(\"sum\");\n      return new Response(\"Revalidated\");\n    }\n    return new Response(\"Not found\", { status: 404 });\n  }),\n}\n```\n\n## API\n\n### `cache(fn, keyParts, options)`\n\nCache the result of an async function. Same API as [`unstable_cache` from Next.js](https://nextjs.org/docs/app/api-reference/functions/unstable_cache).\n\n- `fn`: The async function to cache.\n- `keyParts`: The combination of the function arguments and `keyParts` create the cache key.\n- `options`:\n  - `tags`: Tags which can be used in `revalidateTag`.\n  - `revalidate`: Time in seconds after which the cache should be revalidated.\n\n### `revalidateTag(tag)`\n\nRevalidate all cache entries with the given tag. Revalidation occurs on the next function call.\n\n### `provideCacheToFetch(cacheConfig, handler)`\n\nWrap a fetch handler with the cache configuration.\n\nAlternatively, you can use `provideWaitUntil`, `provideObjectCache`, `provideCacheTagStore` to set async contexts separately. See [src/wrap-fetch.ts](src/wrap-fetch.ts).\n\n### `createCfCacheObjectCache(cache)`\n\nCreate an object cache using the Cloudflare Cache.\n\n### `createCfKvCacheTagStore(kvNamespace)`\n\nCreate a cache tag store using a Cloudflare KV store.\n\n## Write custom cache stores\n\nBy default, the object cache uses Cloudflare Cache, and the tag store uses Cloudflare KV, see [src/cloudflare-adapters.ts](src/cloudflare-adapters.ts). You can write your own implementations by implementing the `ObjectCache` and `CacheTagStore` interfaces.\n\n```ts\nconst customObjectCache: ObjectCache = {\n  async get(key) {\n    // ...\n  },\n  async set(key, value, duration) {\n    // ...\n  },\n}\n\nconst customCacheTagStore: CacheTagStore = {\n  async getTagsCacheKey(tags) {\n    // ...\n  },\n  async revalidateTag(tag) {\n    // ...\n  }\n}\n\nconst cacheConfig: CacheConfig = {\n  objectCache: customObjectCache,\n  cacheTagStore: customCacheTagStore,\n};\n```\n\n## TODO\n\n- Allow usage of `Cache-Tag` revalidation for Cloudflare Enterprise customers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhansottowirtz%2Fcloudflare-workers-cache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhansottowirtz%2Fcloudflare-workers-cache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhansottowirtz%2Fcloudflare-workers-cache/lists"}