The power of caching with Fly

It's no secret that we've collectivity become a society where the word patience in no longer in our vocabulary. We get frustrated by buffering videos and pages that pop together as images slowly load. Let's be honest, we hate waiting. And why should we have to? In a world where packages can be delivered by drones within 30 minutes and a cab (or a date) can be ordered from your smartphone within seconds, we've become incredibly comfortable with instant results in every corner of our lives. So why should websites be any different?

One could argue that the most crucial feature of any website is speed. Waiting a couple of extra seconds for a web page to load can sometimes feel like an eternity. This article discusses how you can use fly.cache methods to rapidly deliver HTTP requests ... reducing page load time and providing your visitors with a smooth, friendly experience.

What Does Caching with Fly Look Like?

Caching with Fly involves storing data, that does not change frequently, on multiple servers across the globe, for the purpose of almost instantaneous retrieval by users ... equating to astoundingly fast page loads. This works by eliminating the need to request data from the origin server every time that data is needed. A typical caching scenario could involve storing a cached copy of a static web page. If the page does not change every five minutes, it makes sense to store it in the cache, ultimately saving time and bandwidth for your users when they want to fire it up. Your users request that static page and get it from whichever fly server is closet to them.

In the case of websites, certain parts of your site (such as a script or logo that appears on every page) can be saved on a CDN, which is a collection of servers distributed across the globe with the purpose of storing and delivering assets faster. Users can fetch what they need to make up your website from the CDN server closest to them, rather than traveling to the origin server every single time, wherever that might be.

Serving a cached page is easily much faster than traveling to the origin server and generating an entire web page from scratch. The ability to cache and reuse previously fetched resources is a critical aspect of optimizing websites for performance.

How to Optimize Your Site with Fly's HTTP Caching

The following example exposes how you can use Fly to cache data from an API ... reducing the number of HTTP requests for this data and dramatically improving website speed. We'll be making an API call for a list of authors that we want to show our users.

// index.js 

fly.http.respondWith(async function() { 
  let cacheStatus
  let key = "author-data"
  let cachedValue = await fly.cache.getString(key)

  if (cachedValue !== null) {
    cacheStatus = "HIT" 
    console.log("cache hit:", key)
    return new Response(cachedValue, { 
      headers: { 
        'x-cache': cacheStatus 
      } 
    }) 
  } else {
    cacheStatus = "MISS" 
    console.log("cache miss:", key) 

    const url = 'https://randomuser.me/api/?results=20'
    const fetchedData = await fetch(url)
    const jsonData = await fetchedData.json()
    const results = jsonData.results

    let authors = []
    for (let i=0; i<results.length; i++) {
      let name = results[i].name.first + " " + results[i].name.last
      authors.push(name)
    }

    let resultsList = authors.join("\n")
    fly.cache.set(key, resultsList, 604800)

    return new Response(resultsList, { 
      headers: { 
        'x-cache': cacheStatus 
      } 
    }) 
  }
}) 

This code demonstrates using fetch to make an http get request to an API, storing the response in the cache and then later retrieving it to display on the page. Let's analyze exactly what's going on here to gain a better understanding of how fly.cache works.

fly.http.respondWith(async function() { 
  let cacheStatus
  let key = versionKey("author-data") 
  let cachedValue = await fly.cache.getString(key)

In our index.js file, declare an async function that we can use to check if there is anything in our cache and set our cache if not. Define the variable cacheStatus which we will use to specify if our fly.cache.get was a "hit" or "miss".

The key variable will correspond with a specific value in our cache. We will use this key to set and get key/value pairs in the cache.

We want to check if there is any author data in our cache to display for the user rather than making a time-consuming API call and waiting for the response.

Our cachedValue variable will be used to retrieve the value in the cache at the specified key. fly.cache.getString allows us to get a string value (or null) from the cache. fly.cache.get allows us to get an ArrayBuffer value (or null) from the cache. Here we are passing in our key variable, author-data.

if (cachedValue !== null) {
cacheStatus = "HIT" 
console.log("cache hit:", key)
return new Response(cachedValue, { 
  headers: { 
    'x-cache': cacheStatus 
  } 
}) 

If the cachedValue is not null, that means that there is a value in our cache at this key and we will serve that cached value to the user.

A CDN (Content Delivery Network) adds the X-cache header to HTTP Responses. X-cache:HIT means that our request was served by the CDN (from the cache), not the origin server. A CDN takes content and distributes that content in lots of places. It's an efficient way to store data on servers around the world and deliver content faster than from a single location. If our cache is not empty at this key, change cacheStatus to "HIT" and return our list of authors to the user at lightning speed!

authors-1

} else {
    cacheStatus = "MISS" 
    console.log("cache miss:", key) 

X-cache:MISS means that your request was not served by the CDN, but rather served from the origin server. If our cache value is null at this key, change cacheStatus to "MISS" and continue.

const url = 'https://randomuser.me/api/?results=20'
const fetchedData = await fetch(url)
const jsonData = await fetchedData.json()
const results = jsonData.results

We've come to the conclusion that our cache is empty (or perhaps expired) at this key, so we'll fetch the data we want from the origin server. Use fetch to make an http request to randomuser.me/api, synchronously wait on a promise and store the response inside our fetchedData variable. Convert the response to JSON and store the JSON data inside our jsonData variable. Grab the results of this data and store it inside results. Right now we don't care about anything else that came through in the response, we just want the list of authors.

let authors = []
for (let i=0; i<results.length; i++) {
  let name = results[i].name.first + " " + results[i].name.last
  authors.push(name)
}

Create the empty array authors. We will use this to sort through our data and keep only the info we want to show the user. In this case, we are keeping the first and last name of every author.

let resultsList = authors.join("\n")
fly.cache.set(key, resultsList, 604800)

Define the variable resultsList that will be equal to our list of authors, each on their own line.

Now let's set that cache! fly.cache.set sets a value at a certain key, with an optional ttl (time to live). It takes three parameters:

fly.cache.set(key, value, ttl)

  1. Key: This will be a string equal to the key to add or overwrite.
  2. Value: This will be a string or ArrayBuffer equal to the data you want to store at the specified key. The data can be up to 2MB in size.
  3. TTL – in seconds: Time to live is optional and must be equal to a number.

Our set function will return a promise which will be true if the set was successful.

We will be storing our resultsList at the key author-data with a ttl of 604800 seconds (1 week). You can set the ttl to any amount of time you'd like ... 24 hours, 1 week, 1 month, etc ...

return new Response(resultsList, { 
  headers: { 
    'x-cache': cacheStatus 
  } 
}) 
}
}) 

Finally, return our resultsList to the user. The response here is served by the origin, not the cache ... which will take longer to appear on the page.

The good news is that we stored our resultsList in the cache for 1 week. So any user who views this page during this week will be served the cached version of the page very quickly. When the week is over, the cache will reset with a new list of authors and save for another week and so on ... This could be helpful if you know that this list of authors changes every single week. Instead of having your users make an API call for this data every single time they want it, add it to the cache and automatically reset the cache every time the data changes.

Cache Them If You Can

Overall, the fly.cache uses the simple key/value method to store data in each edge location, all over the world. It can be particularly useful for storing fully rendered versions of back end data, entire web pages, scripts and other assets.

Caching is one of the most powerful tools available to websites today. The performance of your site can be astronomically improved by serving cache resources. Also, keep in mind that, by implementing caching, you aren’t just making your website faster and more responsive, you're equipping it for better SEO scores, increased user satisfaction, better conversions and therefore increased sales if you’re selling products or services online. So remember, the fastest way to make HTTP requests is to not make them at all ;)