Home The case for RDF Label Cache

Why RDF Label Cache?

Presenting RDF in a human readable form requires retrieving labels for all IRIs.

The usual ways to get labels - and why they hurt

1. Hard-code labels into the app

Ship a big { iri: "label" } table in the frontend.

Why it's bad: stale the moment a vocabulary changes, duplicated in every app you build, no shared source of truth, needs a redeploy to fix a typo - and it only covers the IRIs you thought of in advance.

2. Ask for labels in the query (complicated SPARQL)

For every IRI you display, add an OPTIONAL block pulling its rdfs:label / skos:prefLabel - with language-fallback logic - and return the labels alongside the data.

Why it's bad: the query balloons - every term needs its own OPTIONAL + COALESCE, language fallback in SPARQL adds additional bloat, it's slow, and you paste the same label boilerplate into every query.

3. A follow-up query for the labels

Run the main query, collect the IRIs it returned, then query the graph again for their labels.

Trade-off: this adds a second round trip to the graph and makes rendering depend on it again.

4. Shadow “label” named graphs

Materialise a named graph - e.g. one per focus node - that holds all the labels for its IRIs, so you can pull them alongside the data.

Why it's bad: now you maintain a copy. It drifts from the source, bloats storage, needs a sync/materialisation pipeline to stay current - and it's still inside the triplestore, so every read still lands on the KG.

What RDF Label Cache does instead

Follow-up lookups - from an edge CDN, not your KG

Take approach 3 (a per-IRI lookup) and move it off the triplestore entirely. Labels are pre-materialised once into blob storage (R2) and fronted by a tiered edge cache. A lookup is a plain GET /label?iri=…, resolved at the nearest edge location.

Data Your app query Knowledge graph results (opaque IRIs)
Labels Your app GET /label?iri=… Label cache edge cache R2
Labels come from a globally distributed edge CDN, not your triplestore.

What that buys you: a predictable cacheable read path

100k/dayWorkers Free
daily request allowance
$5/monthWorkers Paid
includes 10 million requests
tieredWorkers Cache
hits skip Worker execution and R2

It's a shared edge cache, not per-user.

Cloudflare caches each label response per data centre, shared across everyone routed there. The first request for an IRI in a region warms the cache; with a long TTL, every later user in that region is served the cached copy - the request never reaches R2. Tiered caching aggregates this across the network, so cold locations pull from a regional tier rather than round-tripping to storage. Since the same vocabulary IRIs (rdfs:label, schema:name, …) appear in everyone's data, hit rates on popular terms are very high.

Pricing follows documented platform units.

The Workers Free plan currently allows 100,000 requests per day. The Paid plan starts at $5 per month, includes 10 million monthly requests, and charges $0.30 per additional million; CPU charges can also apply. R2 has separate storage and operation pricing, with no Internet egress fee for direct R2 access. A cache hit still counts as a Workers request, but it avoids Worker CPU and an R2 read. Check the Workers and R2 pricing pages for current rates.

Cache hits avoid the application backend.

A Workers Cache hit is returned before the Worker runs, so it avoids both application execution and an R2 lookup. Cloudflare operates a globally distributed network, but this project does not publish a latency percentile: measure warm and cold behavior from the regions and clients relevant to your application.

Try the demo  ·  Getting started  ·  Source & design docs