How to get cache metadata from nested render array when returning response? I have a controller action that renders a list nodes and returns them, and only them. It's invoked via AJAX and the JS code just takes the result and inserts it into the page somewhere.

My problem is getting the cache metadata from the render array of nodes and passing it to the response. Here's the simplified action:

public function myControllerAction() {
  $builder = \Drupal::entityTypeManager()-&gt;getViewBuilder('node');
  // Get render array containing a few nodes.
  $nodes = $builder-&gt;viewMultiple([1,2,3], 'teaser');

  $response = new CacheableResponse();
  $response-&gt;setCacheableMetadata(CacheableMetadata::createFromRenderArray($nodes));
  return $response;
}


The problem is CacheableMetadata::createFromRenderArray() expects you to not give it a nested render array, but that's what the view builder returns. It returns an array of render arrays.

How can I efficiently extract the metadata from each node in the list and apply it to the response?
Render the nodes in a render context, so that the metadata can bubble up:

  $storage = \Drupal::entityTypeManager()-&gt;getStorage('node');
  $builder = \Drupal::entityTypeManager()-&gt;getViewBuilder('node');
  $nodes = $storage-&gt;loadMultiple([1,2,3]);
  $build = $builder-&gt;viewMultiple($nodes, 'teaser');

  $render_context = new \Drupal\Core\Render\RenderContext();
  \Drupal::service('renderer')-&gt;executeInRenderContext($render_context, function() use (&amp;$build) {
    \Drupal::service('renderer')-&gt;render($build, TRUE);
  });

  $response = new CacheableResponse();
  if (!$render_context-&gt;isEmpty()) {
    $response-&gt;addCacheableDependency($render_context-&gt;pop());
  }


The rendering is not lost. The rendered nodes are in $build['#markup'] and you can add the html to the response if that is what you want to do:

$response-&gt;setContent($build['#markup']);




Edit:

Just seen in the related question on the right a similar topic. While the above code is good for demonstration purpose, because it shows how to get cache metadata from a nested render array, the simpler code in the answer to How to handle cache metadata in an AJAX response? is actually working for this nested build array too, because the dependencies of the build array are added after rendering and at this point the metadata has bubbled up to the top level.