Caching has always been one of the many optimization tricks widely used in software development. In this post, we will look at how to enable server-side caching in Expressjs. Read on.

Server Side cache — Basic Idea

Web apps often access the server to get new data to render in the DOM. This puts a heavy load on the server since it requires heavy processing power to produce dynamic results.

Browsers on their side make use of caching to prevent frequent server requests. Browsers have an internal caching mechanism that enables them to store responses of requests and to respond with the cached response when the same request is made. this helps to reduce frequent server operations from the same requests.

Though browsers help but the server needs to run and render for different browsers and users. The server-side has to find a way to optimize long operations. This is done through the use of caching.

What is Caching?

Before we delve into server-side caching let’s first understand what caching means.

Caching is the concept of storing the result of a CPU-intensive operation so that in the next execution of the same operation with the same input, the result is returned from the storage instead of performing the resource-consuming operation all over again.

In programming, functions are usually cached and this process is called memoization.

function wait(ms) {

var start = new Date().getTime()

var end = start

while(end < start + ms) {

end = new Date().getTime()

}

console.log('done with longOp func')

}



function longOp(input) {

let result = null

// simulating a long operation

wait(1000)

result = 90 * input

return result

} longOp(5) // takes 1000 ms to complete

longOp(50) // takes 1000 ms to complete

longOp(500) // takes 1000 ms to complete

longOp(5000) // takes 1000 ms to complete

// Total ms to run: 4000 ms! longOp(5) // takes 1000 ms to complete

longOp(50) // takes 1000 ms to complete

longOp(500) // takes 1000 ms to complete

longOp(5000) // takes 1000 ms to complete

// Total ms to run: 4000 ms! again

The function above takes approx, 1000 milliseconds to run. We tried calling it with different inputs: 5, 50, 500, 5000. In total it will take 4000 ms for our file to run !! Running the same inputs again will take another 4000 ms!!

Now, since we know that the output depends on the input return input * 90 . We can store the result for each input and return the stored result on the next invocation with the same input.

// ...

const cache = {}

function longOp(input) {

let result = null



// check if the input has been calaculated already and stored in cache

if(cache[input]) {

// return the result in the cache bypassing the long operation

return cache[input]

}

// if not do the operation and store the result

else {

// simulating a long operation

wait(1000)

result = input * 90

// store the result with the input as key

cache[input] = result

}

return result

}

Now, we have modified our longOp function to use caching. The cache object is where we store the result of prev. calc. of an input. When the function is called with an input, we check if the results in the cache object, if yes we return the result bypassing the long operation. If no, we perform the long operation and store the result in the cache object with the input as the key, so on next invocation with the same input, the result will be found in the cache object.

If we run the longOp again:

longOp(5) // takes 1000 ms to complete

longOp(50) // takes 1000 ms to complete

longOp(500) // takes 1000 ms to complete

longOp(5000) // takes 1000 ms to complete

// Total ms to run: 4000 ms! longOp(5) // takes 1 ms to complete

longOp(50) // takes 1 ms to complete

longOp(500) // takes 1 ms to complete

longOp(5000) // takes 1 ms to complete

// Total ms to run: 4 ms! again :)

You see that is a huge performance boost!! from 4000ms to 4ms!! The first part ran without from the cache, the second part just returned the result of the cache operation from the first part.

Now, we have understood what caching means, let’s move on.