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/patientsand 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 →