Caching virtual pages based on content from an API

I really enjoyed reading the new virtual pages guide. The possibilities seem endless especially when connecting Kirby to external APIs.

Right now I am wondering if there is a way to cache API responses locally. The NYT Movie Reviews example works fine on my local machine but I sometimes keep running into performance issues that could have something to do with rate limits enforced by NYT.

How would I got about caching the response locally? Is that even possible? Or does it make sense to turn the virtual pages into static pages that are updated via the API regularly?

Yes, that is possible and makes perfect sense for those cases where you just read from an API.

Read up on caching here: https://getkirby.com/docs/guide/cache

1 Like

These virtual pages also profit from the standard file cache if you enable it.

1 Like

Thanks for your replies and pointing me in the right direction. Using the default pages cache worked for the list view of reviews (probably because it exists as an actual page in the content folder, right?) but not the individual reviews.

I tried setting up a separate cache instance for the API but I seem to be missing something. site/cache/api/apiData.cache exists but the API response is not being written to the file.

Is the page model the right place to initialize this cache? Or am I missing something else?

Config file: site/config/config.php

<?php

return [
  'cache.api' => true
];

Page Model: site/models/reviews.php


<?php

class ReviewsPage extends Page
{
  public function children()
  {
    $results = [];
    $pages   = [];
    $apiKey  = 'superawesomeapikey';

    $apiCache = kirby()->cache('api');
    $apiData  = $apiCache->get('apiData');

    if ($apiData === null) {
      $apiData = Remote::get('https://api.nytimes.com/svc/movies/v2/reviews/picks.json?api-key=' . $apiKey);
      $apiCache->set('apiData', $apiData);
    }

    if ($apiData->code() === 200) {
      $results = $apiData->json(false)->results;
    }

    foreach ($results as $key => $review) {
      $pages[] = [
        'slug'     => Str::slug($review->display_title),
        'num'      => $key+1,
        'template' => 'review',
        'model'    => 'review',
        'content'  => [
          'title'    => $review->display_title,
          'headline' => $review->headline,
          'byline'   => $review->byline,
          'summary'  => $review->summary_short,
          'date'     => $review->publication_date,
          'link'     => $review->link->url,
          'linkText' => $review->link->suggested_link_text,
          'cover'    => $review->multimedia->src
        ]
      ];
    }
    return Pages::factory($pages, $this);
  }
}

Do you figure out something about your issue ? I will need to do (almost) the same for virtual pages fetched from API.

@jesuismaxime In your case, you will probably have a class that handles fetching from the API, check out @bnomei’s Instagram plugin for an example: https://github.com/bnomei/kirby3-instagram