Search code examples
node.jsexpresscachingaxios

Caching in an app which consumes and serves an API


I don't know if this is the best place to ask but.

I am building a weather app which consumes an api using axios and then serves it using express. I wanted to know where should I add caching to improve the speed of the api? Would it be at the axios layer when I am consuming or at the express layer when I am serving.

Below is my code for a little context

import { weatherApiKey } from 'config';
import axios from 'axios';

const forecast = (location, service) => {
    console.log('inside api calling location: ', location);
    axios.get(`http://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${weatherApiKey}`)
        .then(res => {
            service(undefined, res.data)
        })
        .catch(err => {
            service('Error calling weather API');
        })
}

module.exports = forecast;

I am then serving the consumed api via the following.

app.get('/weather', (req, res) => {
    const locale = req.query.locale;

    if(!locale) {
        return res.send({
            error: 'Please provide valid locale'
        })
    }

    foreCast(locale, (err, weatherData) => {
        if(err) {
            console.log('error in calling weather API')
            res.send({err});
        }
        console.log('returning weather data', weatherData)
        res.send({weatherData})
    });
    
})

Solution

  • Yes, generally there are lots of forms and layers to cache at. Given the fact that you're creating an API, I would expect some caching to be applied as close to the consumer as possible. This could be at the CDN level. However, a quick easy answer would be to add something as a cacheable middleware for your express app.

    There are many approaches to populating and invalidating caches, and you take care to plan these specifically for your use case. Try not to prematurely optimise using caching where you can. It introduces complexity, dependencies and can contribute towards hard-to-debug problems when there are lots of caching layers being applied.

    But a simple example would be something like:

    'use strict'
    
    var express = require('express');
    var app = express();
    var mcache = require('memory-cache');
    
    app.set('view engine', 'jade');
    
    var cache = (duration) => {
      return (req, res, next) => {
        let key = '__express__' + req.originalUrl || req.url
        let cachedBody = mcache.get(key)
        if (cachedBody) {
          res.send(cachedBody)
          return
        } else {
          res.sendResponse = res.send
          res.send = (body) => {
            mcache.put(key, body, duration * 1000);
            res.sendResponse(body)
          }
          next()
        }
      }
    }
    
    app.get('/', cache(10), (req, res) => {
      setTimeout(() => {
        res.render('index', { title: 'Hey', message: 'Hello there', date: new Date()})
      }, 5000) //setTimeout was used to simulate a slow processing request
    })
    
    app.get('/user/:id', cache(10), (req, res) => {
      setTimeout(() => {
        if (req.params.id == 1) {
          res.json({ id: 1, name: "John"})
        } else if (req.params.id == 2) {
          res.json({ id: 2, name: "Bob"})
        } else if (req.params.id == 3) {
          res.json({ id: 3, name: "Stuart"})
        }
      }, 3000) //setTimeout was used to simulate a slow processing request
    })
    
    app.use((req, res) => {
      res.status(404).send('') //not found
    })
    
    app.listen(process.env.PORT, function () {
      console.log(`Example app listening on port ${process.env.PORT}!`)
    })
    

    Note: This is taken from https://medium.com/the-node-js-collection/simple-server-side-cache-for-express-js-with-node-js-45ff296ca0f0 using the memory-cache npm package.