Skip to content

Fetching from Your Own API

You have a Flask route that returns JSON. Now use fetch() from the frontend to call it — no page reload, no Jinja2 loop, no template variables.


Two sides of the same app

Your project now has two sides:

  • Backend (app.py) — Flask serves the API at /patients
  • Frontend (templates/index.html) — JavaScript fetches from /patients and displays the data

The Flask app serves both. When the HTML page is open in the browser, its JavaScript calls the API on the same server.


The fetch call

Because the frontend and backend are on the same server, you do not need a full URL — just the path:

fetch("/patients")
  .then(function(response) {
    return response.json()
  })
  .then(function(patients) {
    let tableBody = document.getElementById("patient-rows")
    let html = ""

    for (let patient of patients) {
      html = html + "<tr><td>" + patient.name + "</td><td>" + patient.age + "</td></tr>"
    }

    tableBody.innerHTML = html
  })

Flask receives the request at /patients, queries the database, and returns JSON. The .then() chain converts it and builds the table rows.


The complete files

app.py

from flask import Flask, render_template, jsonify
from database import Patient

app = Flask(__name__)

@app.route('/')
def home():
    return render_template('index.html')

@app.route('/patients')
def get_patients():
    patients = Patient.objects.all()
    result = []
    for patient in patients:
        result.append({ "name": patient.name, "age": patient.age })
    return jsonify(result)

if __name__ == '__main__':
    app.run(debug=True)

templates/index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Patients</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
  </head>
  <body>

    <div class="container mt-4">

      <h1 class="mb-4">Patients</h1>

      <button onclick="loadPatients()" class="btn btn-primary mb-3">Load Patients</button>

      <table class="table table-striped table-bordered">
        <thead>
          <tr>
            <th>Name</th>
            <th>Age</th>
          </tr>
        </thead>
        <tbody id="patient-rows">
        </tbody>
      </table>

    </div>

    <script>
      function loadPatients() {
        fetch("/patients")
          .then(function(response) {
            return response.json()
          })
          .then(function(patients) {
            // get the table body and build rows from the patient data
            let tableBody = document.getElementById("patient-rows")
            let html = ""

            for (let patient of patients) {
              html = html + "<tr><td>" + patient.name + "</td><td>" + patient.age + "</td></tr>"
            }

            tableBody.innerHTML = html
          })
      }
    </script>

  </body>
</html>

What changed from before

The template has no Jinja2 at all. No {% for %}. No {{ patient.name }}. Flask just serves a plain HTML file. The data comes separately — fetched by JavaScript when the user clicks the button.

This is a different way of thinking about Flask. Before: Flask prepared the data and baked it into the template before sending it. Now: Flask sends a plain page, and JavaScript fetches the data separately on demand.

Both approaches are valid. This one is how most modern web apps work.


Challenge

Add a Clear button that sets tableBody.innerHTML = "". Then try calling loadPatients() automatically when the page loads — just call it at the very bottom of your <script> block with no button needed. What happens?


← Building a JSON API with Flask    Next: Simple API Exercises →