Homepage Project - Eleventy Fetch

A (long) while ago I wrote about my browser homepage project (built with Eleventy). I've continued developing it, occasionally adding or removing a panel, ever since, and every now-and-then I have a hack at the underlying code to improve performance.

Screenshot of my homepage app, 2023 version

There`s more content than there used to be!

One of the things that I've always found a little tricky to deal with is trying not to make too many API requests when developing locally — the concern being that I develop by running Eleventy in --serve mode, and by default it tries to completely refresh all API data every time any file is updated.

In the past I've gotten around this by using a .env variable, checking it's value (or existence) in each API query, and serving pre-written dummy data if so. But that leads to very messy code, and means that if an API changes (or goes offline) you don't get any warning in your local development setup — the first you hear of it is when the errors start flowing from Netlify.

Enter Eleventy Fetch.

Eleventy Fetch essentially works by replacing a fetch request (in my case, replacing an axios request). The results of the fetch are cached and Eleventy Fetch will use that cached data until a (user-determined) timeout period. Result: one API call to get 'live' data, and no need for complicated environment variables.

The old code:

const axios = require('axios');

const dummydata = {
    // fallback data
}

module.exports = async function () {

    const livedata = await axios
        .get(<endpoint-url>)
        .then(res => res.data)
        .catch((err) => {
            return dummydata;
        });

    return livedata;
}

becomes:

const EleventyFetch = require("@11ty/eleventy-fetch");

const dummydata = {
    // fallback data (not really needed, but ...)
}

module.exports = async function () {

    try {

        const livedata = await EleventyFetch(<endpoint-url>, {
            duration: "3h",
            type: "json"
        });

        return livedata;

    } catch {

        return dummy_data;
    }
}

The Eleventy Fetch version is no longer, and arguably a little cleaner (I love that it extracts the JSON — or other data format — for you, rather than having to add another line to extract JSON from the returned data).

Having dummydata in there is less important than it used to be (Eleventy Fetch defaults to using cached data, even if the timeout duration has expired, in the event of a fetch error) — but if the data structure is simple I'd rather leave it in there. (I had some structures where the fallback was hundreds of lines, and for those instances I've changed them to return an empty object, array or string dependent on the expected return type).

There is a potential gotcha though — I have one panel which gets a random cat image from thecatapi.com. To ensure that the panel fits, I check the returned image and if the image ratio isn't landscape, request another image — this repeats until an appropriate image is returned. As I now know, an appropriate image is returned about 70% of the time ...

However, when using EleventyFetch on my deployed Netlify build, on the occasions that an 'inappropriate' (square or portrait ratio) image is returned, the request for another image gets ... the cached, inappropriate image. And that's how you burn through 30 minutes of build time before the build errors out. Cue changing that (and only that) fetch back to using axios ...