I would like to display multiple datasets using the Vue.js framework. The datasets come from the FastAPI server backend, containing mocked stock data.
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import polars as pl
from pydantic import BaseModel
from typing import List
app = FastAPI()
# Allow requests from the Vue.js frontend
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"],
allow_methods=["*"],
allow_headers=["*"],
)
class StockDevelopment(BaseModel):
date: str
actual_stock: float
predicted_stock: float
mock_data = pl.DataFrame({
"date": ["2023-09-01", "2023-09-02", "2023-09-03", "2023-09-04", "2023-09-05"],
"actual_stock": [100, 150, 120, 90, 115],
"predicted_stock": [110, 140, 130, 100, 113],
})
@app.get("/data")
async def get_data() -> List[StockDevelopment]:
stock_rows = mock_data.to_dicts()
stocks = [StockDevelopment(**row) for row in stock_rows]
return stocks
The date
, actual_stock
, and the predicted_stock
data frame columns are converted as rows. The stock data can be requested through the <fastapi-server-ipaddress>/data
HTTP endpoint.
How to display the actual and the predicted stock next to each other? Similar to the following picture:
Use the vue-chartjs
alongside with the chart.js
package. Install them with the following command:
npm install vue-chartjs chart.js
After that, import it into the Vue component. However, keep in mind that there is a problem while displaying the API data on the bar chart. Namely, Chart.js tries to render your chart and access the chart data synchronously, so your chart mounts before the API data arrives.
Prevent this by using the v-if
conditional rendering. For more information check out the documentation. Now, create the bar chart component with a loaded, a data, and an option prop.
<template>
<div>
<h1>Stock Data</h1>
<!-- The v-if is used to conditionally render the block -->
<Bar id="my-chart-id" v-if="loaded" :options="chartOptions" :data="chartData" :width="600" />
</div>
</template>
<script>
import { Bar } from 'vue-chartjs'
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js'
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
export default {
name: 'BarChart',
components: { Bar },
data: () => ({
// Prevents chart to mount before the data arrives from API endpoint
loaded: false,
chartData: {
labels: [],
datasets: [
{
label: 'Actual',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
data: []
},
{
label: 'Predicted',
backgroundColor: 'rgba(54, 162, 235, 0.2)',
data: []
}
]
},
chartOptions: {
responsive: true
}
}),
async mounted() {
const apiUrl = 'http://localhost:8000/data'
// Make an HTTP request to fetch the data from the FastAPI server
await fetch(apiUrl)
.then((response) => response.json())
.then((data) => {
// Extract data from the API response and update the chartData
this.chartData.labels = data.map((stock) => stock.date)
this.chartData.datasets[0].data = data.map((stock) => stock.actual_stock)
this.chartData.datasets[1].data = data.map((stock) => stock.predicted_stock)
// Allow the chart to display the data from the API endpoint
this.loaded = true
})
.catch((error) => {
console.error('Error fetching data:', error)
})
}
}
</script>
The bar chart is only loaded when the data is arrived without any error. It is achieved by setting the loaded
prop to true
after fetching the data from the API endpoint. The datasets' values are set during the HTTP request and populated each data using the map
function. If you would like to add more datasets then extend the chartData.datasets
array with another entry. Then, you can assign data to this.chartData.datasets[2].data
array.
Note: the
stock.date
,stock.actual_stock
, and thestock.predicted_stock
return values have to match their corresponding FastAPI response data, described in theStockDevelopment
class.