const express = require("express"); const axios = require("axios"); const redis = require("redis"); const cors = require("cors"); const { param, validationResult } = require("express-validator"); const app = express(); // Redis setup const redisConfig = { url: process.env.REDIS_URL, database: 1, }; let redisClient; (async () => { redisClient = redis.createClient(redisConfig); redisClient.on("error", (error) => console.error(`Error: ${error}`)); await redisClient.connect(); })(); // CORS app.use(cors()); // Functions // Legacy fetch function function LeagueSeason(league, season) { this.url = "https://api-football-v1.p.rapidapi.com/v3/standings"; this.method = "GET"; this.params = { league: league, season: season, }; this.headers = { "X-RapidAPI-Host": process.env.API_HOST, "X-RapidAPI-Key": process.env.API_KEY, }; } // Fetch table from remote API async function fetchTable(league, season) { reqSeason = new LeagueSeason(league, season); const apiResponse = await axios.request(reqSeason); return apiResponse.data; } // Get a league table async function getLeagueTable(req, res) { const league = req.params.league; const season = req.params.season; const seasontable = req.params.league + req.params.season; let results; let isCached = false; // Try to fetch a league table try { // First check cache const cacheResults = await redisClient.get(seasontable); if (cacheResults) { isCached = true; results = JSON.parse(cacheResults); } else { // Fetch remotely results = await fetchTable(league, season); if (results.length === 0) { throw "API returned no data"; } // Store table in cache await redisClient.set(seasontable, JSON.stringify(results), { EX: 21600, NX: true, }); } // Return the league table for the corresponding season res.status(200).send({ fromCache: isCached, table: results, }); } catch (error) { console.error(error); res.status(404).send("Data not found"); } } // Routes // new method app.get("/league/:league/:season", getLeagueTable); // Current Premier League Table app.get("/pl", getLeagueTable); app.get( "/v2/:league/:year", [ // League id must be one of these param("league").isIn(["39", "40", "41", "42"]), // Year must be no earlier than 2018 param("year").isIn([ "2018", "2019", "2020", "2021", "2022", "2023", "2024", ]), ], async (req, res) => { // Validate const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(422).json({ errors: errors.array() }); } // Begin method const league = req.params.league; const year = req.params.year; const query = league + year; try { client.get(query, async (err, leagueTable) => { if (err) throw err; // Return cached league table if present if (leagueTable) { res.status(200).send({ table: JSON.parse(leagueTable), message: "data retrieved from cache", }); } else { // Fetch from the API reqSeason = new LeagueSeason(league, year); const leagueTable = await axios.get( "https://v3.football.api-sports.io/standings", reqSeason ); // Save result to cache client.setex(query, 43200, JSON.stringify(leagueTable.data)); // Return data from API res.status(200).send({ table: leagueTable.data, message: "cache miss", }); } }); } catch (err) { res.status(500).send({ message: err.message }); } } ); app.listen(process.env.PORT || 3001, () => { console.log(`Server running`); });