Skip to content

Getting started

This guide walks through adding Cache to a Layeron backend application. You will declare a cache instance, register it, cache a route response, attach tags, read stats, and purge entries after data changes.

Import cache from @layeron/modules. Declare the cache with a stable logical name, then register it with app.use(...).

Terminal window
import { backend } from "@layeron/core"
import { cache } from "@layeron/modules"
const app = backend({
project: "store-api",
})
const apiCache = cache({
name: "public-api",
namespace: "storefront",
ttlSeconds: 60,
staleWhileRevalidateSeconds: 30,
vary: ["accept-language"],
tags: ["public-api"],
})
app.use(apiCache)

The cache instance identity is storefront/public-api. See Namespaces for platform namespace defaults and naming rules.

Use match(request) at the start of a read-heavy route. Use put(request, response) after generating the response.

Terminal window
app.get("/api/products", async (request) => {
const cached = await apiCache.match(request)
if (cached) {
return cached
}
const products = await listProducts()
const response = Response.json({ products })
await apiCache.put(request, response.clone(), {
tags: ["products"],
ttlSeconds: 60,
})
return response
})

Clone the response before put when the same response object will also be returned from the route.

Tags are invalidation metadata. They help you purge groups of entries after a write.

Terminal window
app.get("/api/products/:id", async (request) => {
const pathSegments = new URL(request.url).pathname.split("/")
const id = pathSegments[3]
const cached = await apiCache.match(request)
if (cached) {
return cached
}
const product = await findProduct(id)
const response = Response.json({ product })
await apiCache.put(request, response.clone(), {
tags: ["products", `product:${id}`],
ttlSeconds: 120,
})
return response
})

Tags stay outside cache-key partitioning. The cache key comes from the request, method, namespace, and vary headers.

After a write updates data, purge the affected tags:

Terminal window
app.post("/api/products/:id", async (request) => {
const pathSegments = new URL(request.url).pathname.split("/")
const id = pathSegments[3]
const input = await request.json()
await updateProduct(id, input)
await apiCache.purge({
tags: ["products", `product:${id}`],
})
return Response.json({ ok: true })
})

purge returns the number of entries Layeron attempted to remove from Cloudflare Cache:

Terminal window
const result = await apiCache.purge({
tags: ["products"],
})
return Response.json(result)

Use delete(request) when a route knows the exact request URL to remove:

Terminal window
await apiCache.delete("https://api.example.com/api/products")

The request must produce the same key as the original write, including method and vary headers.

stats() returns runtime counters for the cache instance:

Terminal window
app.get("/internal/cache/stats", async () => {
const stats = await apiCache.stats()
return Response.json(stats)
})

Example response:

Terminal window
{
"hits": 1204,
"misses": 97,
"puts": 97,
"deletes": 4,
"purges": 12
}
  • Core concepts: Understand instances, keys, TTLs, stale windows, tags, route rules, and state.
  • Keys and vary: Design stable cache keys for routes, queries, headers, and per-call options.
  • Route rules: Attach cache policy to backend routes.
  • Invalidation: Delete one entry or purge by request, tag, or prefix.
  • Stats and observability: Read counters and emit product metrics for cache behavior.