Skip to content

Commit

Permalink
fix(BE): Fixed some issues with caching and consildated fetchFromAPI …
Browse files Browse the repository at this point in the history
…method
  • Loading branch information
FinnbarHome committed Jan 31, 2024
1 parent 127b5c7 commit cb0876f
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 78 deletions.
38 changes: 35 additions & 3 deletions be/cacheUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,26 @@ const NodeCache = require("node-cache");
const cache = new NodeCache({ stdTTL: 600, checkperiod: 120 });
console.log("Cache initialized with TTL 600s and check period 120s");

function getCacheKey(endpoint, data, method) {
const sortedDataString = JSON.stringify(data, Object.keys(data).sort());
function getCacheKey(endpoint, requestData, method) {
let keyData = requestData;

// If the method is POST, use the body of the request for cache key
if (method === 'POST' && requestData.body) {
keyData = requestData.body;
}

const sortedDataString = JSON.stringify(keyData, (key, value) =>
// Sort the object keys if the value is an object
(typeof value === 'object' && value !== null) ? Object.keys(value).sort().reduce((sorted, key) => {
sorted[key] = value[key];
return sorted;
}, {}) : value
);

return `${endpoint}:${sortedDataString}:${method}`;
}


function getCachedData(key) {
const data = cache.get(key);
if (!data) {
Expand All @@ -22,8 +37,25 @@ function setCachedData(key, data) {
}
}

async function withCache(asyncFn, endpoint, data, method) {
const cacheKey = getCacheKey(endpoint, data, method);
let cachedData = getCachedData(cacheKey);

if (cachedData) {
console.log(`Cache hit for ${cacheKey}`);
return cachedData;
} else {
console.log(`Cache miss for ${cacheKey}`);
const result = await asyncFn(endpoint, data, method);
setCachedData(cacheKey, result);
return result;
}
}


module.exports = {
getCacheKey,
getCachedData,
setCachedData
setCachedData,
withCache
};
104 changes: 56 additions & 48 deletions be/routes.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const express = require("express");
const router = express.Router();
const { fetchFromAPI, formatDataForDisplay } = require("./utils");
const { getCacheKey, getCachedData, setCachedData } = require('./cacheUtil');
const { getCacheKey, getCachedData, setCachedData, withCache } = require('./cacheUtil');

Check failure on line 4 in be/routes.js

View workflow job for this annotation

GitHub Actions / BE-build

'getCacheKey' is assigned a value but never used



const axios = require("axios");
Expand Down Expand Up @@ -29,32 +30,29 @@ router.get("/", (req, res) => {
res.send("Hello World!");
});

// backend endpoint for getting branches from API
router.get("/branches", async (req, res) => {
try {
// fetching branch data from the API and returning as JSON
const data = await fetchFromAPI("branches", req.query);
res.json(data);
} catch (error) {
// error handling for fetch operations
res.status(500).send("Error processing request");
}
try {
const requestData = { params: req.query };
const data = await withCache(fetchFromAPI, "branches", requestData, 'GET');
res.json(data);
} catch (error) {
res.status(500).send("Error processing request");
}
});


// backend endpoint for getting ATMs from API
router.get("/atms", async (req, res) => {
console.log("GET /atms called with query params:", req.query);

try {
// fetching ATM data from the API and returning as JSON
const data = await fetchFromAPI("atms", req.query);
res.json(data);
} catch (error) {
// error handling for fetch operations
res.status(500).send("Error processing request");
}
try {
const requestData = { params: req.query };
const data = await withCache(fetchFromAPI, "atms", requestData, 'GET');
res.json(data);
} catch (error) {
res.status(500).send("Error processing request");
}
});


// endpoint to get formatted data for list view
router.get("/list-view-data", (req, res) => {
try {
Expand Down Expand Up @@ -92,10 +90,8 @@ function createFilterConfig(apiEndpoint, filterCriteria) {
};
}

// Route for getting filtered ATMs
router.post("/atms/filter", async (req, res) => {
try {
// Specific structure for ATM request body
const atmFilterCriteria = {
Accessibility: req.body.Accessibility,
ATMServices: req.body.ATMServices,
Expand All @@ -105,31 +101,22 @@ router.post("/atms/filter", async (req, res) => {
Radius: req.body.Radius,
};

const cacheKey = getCacheKey("atms/filter", atmFilterCriteria, "POST");
let cachedData = getCachedData(cacheKey);

if (cachedData) {
console.log(`Cache hit for ${cacheKey} in /atms/filter`);
return res.json(cachedData);
}

const filterConfig = createFilterConfig("atms/filter", atmFilterCriteria);
const filteredAtms = await axios(filterConfig);
setCachedData(cacheKey, filteredAtms.data);
res.json(filteredAtms.data);
const requestData = {
body: atmFilterCriteria,
headers: { "Content-Type": "application/json" }
};

const data = await withCache(fetchFromAPI, "atms/filter", requestData, 'POST');
res.json(data);
} catch (error) {
console.error("Error in /atms/filter route:", error.message);
res.status(500).send("Error processing request");
}


});

// Route for getting filtered branches

router.post("/branches/filter", async (req, res) => {
try {
// Specific structure for Branch request body
const branchFilterCriteria = {
Accessibility: req.body.Accessibility,
ServiceAndFacility: req.body.ServiceAndFacility,
Expand All @@ -138,26 +125,47 @@ router.post("/branches/filter", async (req, res) => {
Radius: req.body.Radius,
};

const cacheKey = getCacheKey("branches/filter", branchFilterCriteria, "POST");
let cachedData = getCachedData(cacheKey);
const requestData = {
body: branchFilterCriteria,
headers: { "Content-Type": "application/json" }
};

const data = await withCache(fetchFromAPI, "branches/filter", requestData, 'POST');
res.json(data);
} catch (error) {
console.error("Error in /branches/filter route:", error.message);
res.status(500).send("Error processing request");
}
});

if (cachedData) {
console.log(`Cache hit for ${cacheKey} in /branches/filter`);
return res.json(cachedData);
}

const filterConfig = createFilterConfig("branches/filter", branchFilterCriteria);
const filteredBranches = await axios(filterConfig);
setCachedData(cacheKey, filteredBranches.data);
res.json(filteredBranches.data);


// Route for getting filtered branches
router.post("/branches/filter", async (req, res) => {
try {
const branchFilterCriteria = {
Accessibility: req.body.Accessibility,
ServiceAndFacility: req.body.ServiceAndFacility,
Latitude: req.body.Latitude,
Longitude: req.body.Longitude,
Radius: req.body.Radius,
};

// Using withCache but wrapping axios call in a function
const data = await withCache(async () => {
const response = await axios(createFilterConfig("branches/filter", branchFilterCriteria));
return response.data;
}, "branches/filter", branchFilterCriteria, 'POST');

res.json(data);
} catch (error) {
console.error("Error in /branches/filter route:", error.message);
res.status(500).send("Error processing request");
}
});


router.get("/test-cache", async (req, res) => {
const testKey = "testKey";
let cachedData = getCachedData(testKey);
Expand Down
42 changes: 15 additions & 27 deletions be/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,35 @@ const { getCacheKey, getCachedData, setCachedData } = require('./cacheUtil');

const API_BASE_URL = process.env.API_BASE_URL || 'https://wombo-412213.nw.r.appspot.com/api/';

// Modified function to handle both GET and POST requests
async function fetchFromAPI(endpoint, data, method = 'GET') {
const cacheKey = getCacheKey(endpoint, data, method);
let cachedData = getCachedData(cacheKey);

if (cachedData) {
console.log(`Cache hit for ${cacheKey}`);
return cachedData;
} else {
console.log(`Cache miss for ${cacheKey}`);
}


// General function to handle API requests
async function fetchFromAPI(endpoint, requestData, method = 'GET') {
try {
let response;

const url = `${API_BASE_URL}${endpoint}`;
const config = {
timeout: 5000,
headers: requestData.headers || {}, // Custom headers if provided
};

if (method === 'GET') {
response = await axios.get(`${API_BASE_URL}${endpoint}`, {
params: data,
timeout: 5000
});
// Performing a GET request
config.params = requestData.params;
response = await axios.get(url, config);
} else if (method === 'POST') {
response = await axios.post(`${API_BASE_URL}${endpoint}`, data, {
timeout: 5000
});
// Performing a POST request
response = await axios.post(url, requestData.body, config);
}

if (response && response.data) {
console.log(`Caching new data for ${cacheKey}`);
setCachedData(cacheKey, response.data);
return response.data;
} else {
throw new Error('Unexpected response format or no data to cache');
throw new Error('Unexpected response format or no data');
}
} catch (error) {
console.error(`API request error for ${endpoint}:`, error.message);
throw error;
}

console.log(`Caching new data for ${cacheKey}`);
setCachedData(cacheKey, response.data);
return response.data;
}


Expand Down

0 comments on commit cb0876f

Please sign in to comment.