[Chartjs]-How to use JSON response from FastAPI backend to create a chart using Chart.js in the frontend?

1👍

Option 1

Post the request through HTML form and then have the API redirect you to a new page (Jinja2Template), where you can read the data and display the chart. As @tiangolo posted here, when performing a RedirectResponse from a POST request route to GET request route, the response status code has to change to status_code=status.HTTP_303_SEE_OTHER (the example below does that).

Update: Redirection is not really necessary here. You can just render the template as usual:

@app.post("/submitUsingForm", response_class=HTMLResponse)
def submitUsingForm(request: Request, input1: str = Form(...), input2: str = Form(...)):
    return templates.TemplateResponse("chart.html", {"request": request, "json_data": json_data})

Option 2

Use Fetch API to post the request, receive the JSON data and display the chart on the same page.

Both options are demonstrated in the example given below (the data used is sample data for the purposes of this demo; you can change/handle them as required).

app.py

from fastapi import FastAPI, Request, status, Form
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi.responses import RedirectResponse

app = FastAPI()

templates = Jinja2Templates(directory="templates")
json_data = {"jsonarray": [{"name": "Joe", "age": 32}, {"name": "Tom", "age": 34}]}


@app.get("/chart", response_class=HTMLResponse)
def get_chart(request: Request):
    return templates.TemplateResponse("chart.html", {"request": request, "json_data": json_data})

@app.post("/submitUsingFetch")
def submitUsingFetch(request: Request, input1: str = Form(...), input2: str = Form(...)):
    return json_data 

@app.post("/submitUsingForm", response_class=HTMLResponse)
def submitUsingForm(request: Request, input1: str = Form(...), input2: str = Form(...)):
    #redirect_url = request.url_for('get_chart')
    #return RedirectResponse(redirect_url, status_code=status.HTTP_303_SEE_OTHER)
    return templates.TemplateResponse("chart.html", {"request": request, "json_data": json_data}) 

@app.get("/", response_class=HTMLResponse)
def home(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

templates/index.html

<!DOCTYPE html>
<html>
   <head>
      <title> Home</title>
      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
      <script>
         function send_request() {
            const formElems = document.getElementById('my_form');
            var formData = new FormData(formElems);
            const url = "http://127.0.0.1:8000/submitUsingFetch";
            fetch(url, {
                method: 'POST',
                headers: {'Accept': 'application/json'},
                body: formData
                }).then(resp => {
                    return resp.json();
                }).then(body => {
                    display_chart(body)
                }).catch(error => {
                    console.error(error);
                });
         }
         
         function display_chart(json_data) {
            var labels = json_data.jsonarray.map(function(e) {return e.name;});
            var data = json_data.jsonarray.map(function(e) {return e.age;});
            const ctx = document.getElementById('myChart');
            var config = {
               type: 'line',
               data: {
                  labels: labels,
                  datasets: [{
                     label: 'Graph Line',
                     data: data,
                     backgroundColor: 'rgba(0, 119, 204, 0.3)'
                  }]
               }
            };
         
            var chart = new Chart(ctx, config);
         }
      </script>
   </head>
   <body>
      <form action="/submitUsingForm"  method='post' id='my_form'>
         <label>Input 1:</label><br>
         <input type="text" id="input1" name="input1" value="0"><br>
         <label>Input 2:</label><br>
         <input type="text" id="input2" name="input2" value="0"><br>
         <input type="submit" value="Submit using Form">
         <input type="button" value="Submit using Fetch" onclick="send_request()">
      </form>
    <canvas id="myChart"></canvas>
   </body>
</html>

templates/chart.html

<html>
   <head>
      <head>
         <title> Chart</title>
         <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
         <script>
            function display_chart(){
                var json_data = {{json_data|tojson}};
                var labels = json_data.jsonarray.map(function(e) {return e.name;});
                var data = json_data.jsonarray.map(function(e) {return e.age;});
                const ctx = document.getElementById('myChart');
                var config = {
                   type: 'line',
                   data: {
                      labels: labels,
                      datasets: [{
                         label: 'Graph Line',
                         data: data,
                         backgroundColor: 'rgba(0, 119, 204, 0.3)'
                      }]
                   }
                };
                
                var chart = new Chart(ctx, config);
            }
         </script>
   </head>
   <body onload="display_chart()">
    <canvas id="myChart"></canvas>
   </body>
</html>

0👍

Got it to work with the below code. The trick like @The Fool suggested in one of his/her comments

document.forms['myFormId'].addEventListener('submit', (event) => {
    event.preventDefault();
    // TODO do something here to show user that form is being submitted
    fetch(event.target.action, {
        method: 'POST',
        body: new URLSearchParams(new FormData(event.target)) // event.target is the form
    }).then((resp) => {
        return resp.json(); // or resp.text() or whatever the server sends
    }).then((body) => {
        // TODO handle body
    }).catch((error) => {
        // TODO handle error
    });
});

What I did was place my already written JS code within "Todo handle body".
That worked!

Leave a comment