A REST resource with no cache metadata forces every single request through your full Drupal bootstrap and controller logic — even for two identical requests made a second apart. This is the same principle as page and render caching from earlier in this tutorial, applied specifically to API responses: tell Drupal what the response depends on, and it can serve repeat requests without recomputing anything.
What you'll learn in this lesson
- Why a custom REST resource needs explicit cache metadata — it isn't automatic
- Attaching cache tags, contexts, and max-age to a REST response
- Why JSON:API's built-in resources already do this correctly for you
A REST resource with no caching at all
public function get() {
$data = $this->buildResponseData();
return new ResourceResponse($data);
// No cache metadata attached — every request re-runs buildResponseData()
// in full, regardless of whether anything actually changed.
}
Adding real cache metadata
public function get(NodeInterface $node) {
$data = $this->buildResponseData($node);
$response = new ResourceResponse($data);
$cacheable_metadata = CacheableMetadata::createFromObject($node);
$cacheable_metadata->addCacheContexts(['url.query_args']);
$cacheable_metadata->setCacheMaxAge(3600);
$response->addCacheableDependency($cacheable_metadata);
return $response;
}
This is exactly the cache tags/contexts/max-age vocabulary from the Caching Strategies section, applied to a ResourceResponse instead of a render array. CacheableMetadata::createFromObject($node) pulls the node's own cache tags automatically — the response gets invalidated correctly when that specific node changes, without you hand-listing tags yourself.
buildResponseData() method actually depends on — that's information only your code has. Unlike an entity view builder (which automatically knows it's rendering a specific entity), a hand-written REST resource must explicitly declare its own dependencies, or Drupal correctly assumes the response is uncacheable rather than risk serving stale data.JSON:API already does this correctly
Core's JSON:API module (unlike a hand-written REST resource) already attaches correct cache metadata to every response automatically, derived from the actual entities and fields included — which is exactly why JSON:API is generally the better default choice over hand-rolled REST resources for standard entity CRUD, and why the rest of this section focuses on optimizing JSON:API specifically rather than teaching you to hand-roll cache metadata for every custom resource.
Quick check: you write a custom REST resource with no explicit cache metadata at all. Is the response automatically uncacheable, or does Drupal guess reasonable defaults? If you said automatically uncacheable — Drupal never guesses at cacheability for data it doesn't understand — you've understood exactly why this lesson exists.
Key takeaways
- A custom REST resource has no automatic cache metadata — you must attach it explicitly via
CacheableMetadata. CacheableMetadata::createFromObject()pulls an entity's own cache tags automatically, saving you from hand-listing them.- JSON:API's built-in resources already handle this correctly — a real reason to prefer it over custom REST resources for standard entity access.
- Uncached REST responses re-run their full logic on every single request, identical or not.
Coming up next
Caching handles repeat requests. Next, we look at reducing the cost of any individual JSON:API request in the first place — trimming what it actually fetches and returns.