My photo

Nariman Mani, P.Eng., PhD Computer and Software Engineering
Home

    Visualizing Economic Indicators - A Full-Stack Application with Node.js, React, Express.js, and Next.js

    February 10, 2024

    we'll create a dynamic web application that visualizes two key economic indicators: inflation rates and the Bank of Canada's overnight rates, over a selectable time period. Our application will leverage the Bank of Canada's API for real-time data fetching and display the information using a line graph. We'll employ a Node.js backend to retrieve the data and a React NextJS frontend for rendering the chart

    Backend Development with Express

    Step 1: Initialize Your Node.js Project

    Create a new directory for your project and initialize a Node.js application:

    mkdir economic-indicators
    cd economic-indicators
    npm init -y
    

    Step 2: Install Dependencies We need Express for our server framework and Axios for HTTP requests:

    npm install express axios
    

    Step 3: Create the Express Server In your project directory, create a file named server.js. This server will have two routes: one for inflation data and another for overnight rates.

    Inflation Data Route:

    
    const express = require('express');
    const axios = require('axios');
    const app = express();
    const port = 3001; // Different from Next.js port
    
    app.get('/api/inflation', async (req, res) => {
        const months = req.query.months || '120';
        const url = `https://www.bankofcanada.ca/valet/observations/group/CPI_MONTHLY/json?recent=${months}`;
        try {
            const response = await axios.get(url);
            const data = response.data;
            res.json({
                dates: data.observations.map(obs => obs.d),
                rates: data.observations.map(obs => obs.CPIW ? obs.CPIW.v : null)
            });
        } catch (error) {
            console.error(error);
            res.status(500).send('Error fetching inflation data');
        }
    });
    

    Overnight Rates Route:

    app.get('/api/overnight-rates', async (req, res) => {
        const months = req.query.months || '120';
        const apiURL = `https://www.bankofcanada.ca/valet/observations/V122514/json?recent=${months}`;
        try {
            const response = await axios.get(apiURL);
            const data = response.data;
            res.json({
                dates: data.observations.map(obs => obs.d),
                rates: data.observations.map(obs => obs.V122514.v)
            });
        } catch (error) {
            console.error('Error fetching overnight rates:', error);
            res.status(500).send('Error fetching overnight rates');
        }
    });
    
    app.listen(port, () => {
        console.log(`Server running on http://localhost:${port}`);
    });
    

    Step 4: Run Your Server Start your server with:

    node server.js
    

    Frontend Development with React and Next.js

    Step 1: Create Your Next.js App In a new directory, initialize your Next.js app:

    npx create-next-app@latest frontend
    cd frontend
    

    Step 2: Install Frontend Dependencies Install Axios for making HTTP requests, react-chartjs-2 for charting, and chart.js:

    npm install axios react-chartjs-2 chart.js
    

    Step 3: Implement the Chart Component Create a new file InflationChart.js in the components directory of your Next.js app. Use the provided React component code to implement the chart logic, which fetches data from your backend and displays it using react-chartjs-2. Below code also includes the implementation of a function to export the chart as a PDF document.

    import React, { useState, useEffect } from 'react';
    import axios from 'axios';
    import { Line } from 'react-chartjs-2';
    import html2canvas from 'html2canvas';
    import jsPDF from 'jspdf';
    
    import {
        Chart as ChartJS,
        CategoryScale,
        LinearScale,
        PointElement,
        LineElement,
        Title,
        Tooltip,
        Legend,
      } from 'chart.js';
      
      ChartJS.register(
        CategoryScale,
        LinearScale,
        PointElement,
        LineElement,
        Title,
        Tooltip,
        Legend
      );
    
      const InflationChart = () => {
        const [chartData, setChartData] = useState({ labels: [], datasets: [] });
        const [selectedMonths, setSelectedMonths] = useState(120);
    
    
        const fetchData = (months) => {
            const fetchInflationData = axios.get(`http://localhost:3002/api/inflation?months=${months}`);
            const fetchOvernightRates = axios.get(`http://localhost:3002/api/overnight-rates?months=${months}`);
    
            Promise.all([fetchInflationData, fetchOvernightRates])
            .then(responses => {
                const inflationData = responses[0].data;
                const overnightRatesData = responses[1].data;
    
             // Combine the data
             const combinedData = inflationData.dates.map((date, index) => ({
                date: date,
                inflationRate: parseFloat(inflationData.rates[index]),
                overnightRate: parseFloat(overnightRatesData.rates[index])
            }));
    
            // Sort the combined data by date
            combinedData.sort((a, b) => new Date(a.date) - new Date(b.date));
    
    
            setChartData({
                labels: combinedData.map(item => item.date),
                datasets: [
                    {
                        label: 'Inflation Rate',
                        data: combinedData.map(item => item.inflationRate),
                        borderColor: 'rgba(75, 192, 192, 1)',
                        backgroundColor: 'rgba(75, 192, 192, 0.5)',
                        yAxisID: 'y1',
                    },
                    {
                        label: 'Overnight Rate',
                        data: combinedData.map(item => item.overnightRate),
                        borderColor: 'rgba(255, 99, 132, 1)',
                        backgroundColor: 'rgba(255, 99, 132, 0.5)',
                        yAxisID: 'y2',
                    }
                ]
            });
        })
                .catch(error => console.error('Error fetching data:', error));
        };
    
    
        useEffect(() => {
            fetchData(selectedMonths);
        }, [selectedMonths]);
        const handleMonthSelectionChange = (event) => {
            setSelectedMonths(event.target.value);
        };
    
    
        const exportChartToPDF = () => {
            const input = document.getElementById('chart-container'); // The element containing the chart
            html2canvas(input, { scale: 5 })
                .then((canvas) => {
                    const imgData = canvas.toDataURL('image/png');
                    const pdf = new jsPDF();
    
                       // Get PDF page dimensions
                       const pdfWidth = pdf.internal.pageSize.getWidth();
                       const pdfHeight = pdf.internal.pageSize.getHeight();
       
                       // Calculate scaled dimensions to maintain aspect ratio
                       const canvasAspectRatio = canvas.width / canvas.height;
                       let imgWidth = pdfWidth * 3/4;
                       let imgHeight = imgWidth / canvasAspectRatio;
       
                       // Check if height exceeds page height, adjust accordingly
                       if (imgHeight > pdfHeight) {
                           imgHeight = pdfHeight;
                           imgWidth = imgHeight * canvasAspectRatio;
                       }
       
                       // Calculate positions to center the image
                       const x = (pdfWidth - imgWidth) / 2;
                       const y = (pdfHeight - imgHeight) /5;
    
                    pdf.addImage(imgData, 'PNG', x, y, imgWidth, imgHeight,'','FAST');
                    pdf.save("chart.pdf");
                })
                .catch(err => {
                    console.error('Error exporting chart:', err);
                });
        };
    
        return (
            <div>
            <div >
           
            <div id="chart-container" >
            <h1 className="users-heading">Inflation and Overnight Rate Over Time</h1>
            <Line
                data={chartData}
                options={{
                    scales: {
                        y1: {
                            type: 'linear',
                            display: true,
                            position: 'left',
                        },
                        y2: {
                            type: 'linear',
                            display: true,
                            position: 'right',
                            grid: {
                                drawOnChartArea: false,
                            },
                        },
                    },
                }}
            />
            </div>
            <div>
                {[12, 24, 36, 48, 60, 72, 84, 96, 108, 120].map(month => (
                    <label key={month}>
                        <input
                            type="radio"
                            value={month}
                            checked={selectedMonths === month.toString()}
                            onChange={handleMonthSelectionChange}
                        />
                        {month} Months
                    </label>
                ))}
            </div>
        </div>
        <button onClick={exportChartToPDF}>Export as PDF</button>
      
        </div>
        
        );
    };
    
    export default InflationChart;
    
    

    Step 4: Display the Chart Edit the pages/index.js file to import and display your InflationChart component.

    import React from 'react';
    import '../styles/UserStyles.css'; // Import the CSS
    import InflationChart from '../components/InflationChartC'; // Adjust the import path as needed
    
    const Inflation = () => {
        return (
            <div className="users-container">
          
                <InflationChart />
            </div>
        );
    };
    
    export default Inflation;
    

    Step 5: Run Your Next.js App Start your Next.js application:

    npm run dev
    

    Your application will now be accessible at http://localhost:3000, and it should display the inflation and overnight rates chart using data fetched from your Express backend.

    Conclusion

    Congratulations! You've built a full-stack web application that visualizes real-time economic data from the Bank of Canada. This project showcased how to set up a simple Express backend to fetch and serve API data, and a React frontend with Next.js to display this data in a dynamic chart. This tutorial provides a foundation for building more complex full-stack applications using these technologies.

2024 All rights reserved.