Hello! Welcome to our next lesson.
In our last session, we established the "big picture" of how web applications work by exploring the client-server model. You learned that your browser is the client (making requests) and our Flask application is the server (providing responses). We saw how a simple request for a webpage involves the browser asking for the HTML file, and the server sending it back.
Today, we're going to make that connection truly interactive. The goal of our application is to take text from a user, process it on our server with Python (and eventually an AI), and show the result. A full page reload every time the user clicks "analyze" would be slow and clunky.
This lesson bridges that gap. You will learn how to use the JavaScript fetch API to send the user's input from the browser to your Flask backend endpoint without reloading the page. This is the core technique for building modern, responsive web applications.
The Problem with Standard HTML Forms
By default, when you put a text area and a button inside an HTML <form> tag, clicking the button will package up all the form's data and send it to the server. The problem? The browser then navigates away from the current page and waits for a brand new page from the server as a response. This full-page refresh is exactly what we want to avoid.
The Solution: Asynchronous Communication with fetch
To create a smoother experience, we can use JavaScript to take control of the submission process. Instead of letting the browser do its default refresh, we'll intercept the user's click, grab the data ourselves, and send it to the server quietly in the background.
The tool for this job is the fetch API. It's a modern feature built into all browsers that lets you make HTTP requests (like the ones we discussed in the last lesson) directly from your JavaScript code.
Let's walk through the three main parts of this process:
- Client-Side (JavaScript): Capture the user's input.
- Client-Side (JavaScript): Send the input to the server using
fetch. - Server-Side (Flask): Receive and process the data.
Part 1: Capturing User Input with JavaScript
First, we need our JavaScript to run when the user clicks the submit button. We also need to tell the browser not to do its default page reload.
The following video explains this first step perfectly. It shows how to listen for a form's "submit" event and how to use event.preventDefault() to stop the refresh.
POST Form Data as JSON with Fetch API in JavaScript
This video from ByteGrad clearly demonstrates how to attach an event listener to a form and prevent the default refresh, which is the essential first step for handling form submissions with JavaScript.
Watch from 00:39 to 02:20. Focus on how addEventListener is used on the form element and why event.preventDefault() is so important.
Once we've stopped the default refresh, we can grab the text from our text area. If our HTML text area has an ID like <textarea id="user-text"></textarea>, we can get its content in JavaScript like this:
const textArea = document.getElementById('user-text');
const userText = textArea.value;
Part 2: Sending Data to the Server with fetch
Now for the main event. With the user's text in a variable, we can use fetch to send it to a specific URL on our Flask server. Since we are sending data to create or process something, we'll use the POST method.
The fetch call for sending data looks like this:
// A JavaScript object to hold our data
const data = {
text: userText
};
// The fetch request
fetch('/api/analyze', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
This might look complex, but each part has a clear purpose. The following article provides an excellent text-based breakdown of what each line does.
Handling User Input and API Calls with JavaScript
The CodeSignal article 'Handling User Input and API Calls with JavaScript' has a fantastic breakdown of the fetch request, explaining each part of the call in a very clear way.
Read the section titled 'Working with the Fetch API'. Focus on the breakdown of the fetch() request, which explains the URL, method, headers, and body.
Here is a summary of those key fetch options:
/api/analyze: This is the URL (or "endpoint") on our server that we are sending the data to. We will create this in Flask next.method: 'POST': We're telling the server that we are sending data, not just requesting a page.headers: { 'Content-Type': 'application/json' }: This is like a label on a package. It tells our Flask server that the data in the body is in JSON format. This is very important!body: JSON.stringify(data): Thebodycontains the actual data. We must convert our JavaScript object into a JSON string before sending it over the network.
Part 3: Receiving Data in Flask
Our JavaScript is now sending data, but our Flask server doesn't know how to catch it yet. We need to create the /api/analyze endpoint and tell it to accept POST requests.
Inside our app.py, we can add a new route:
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
# ... (our existing @app.route('/') route) ...
@app.route('/api/analyze', methods=['POST'])
def analyze_text():
# 1. Get the JSON data sent from the JavaScript
data = request.get_json()
# 2. Extract the text from the data
text_from_user = data['text']
# For now, let's just print it to our terminal to see if it worked
print(f"Received text: {text_from_user}")
# 3. Send a confirmation response back to the client
return jsonify({'status': 'success', 'message': 'Text received!'})
The key parts here are:
methods=['POST']: This explicitly allows this route to handle POST requests.request.get_json(): Flask'srequestobject has a handy.get_json()method that automatically parses the JSON data sent by ourfetchcall into a Python dictionary.
To see the official pattern for this, you can check Flask's own documentation.
The official Flask documentation provides a concise example of how a Flask view can receive JSON data. This completes the loop by showing what happens on the server.
Find the section titled 'Receiving JSON in Views'. This short section contains a code snippet showing how to use request.json to access the data sent from your JavaScript fetch call.
Putting It All Together
Let's combine everything into a working example. Update your project with the following files.
1. app.py
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
# Route to serve the main HTML page
@app.route('/')
def home():
return render_template('index.html')
# API endpoint to handle the text analysis
@app.route('/api/analyze', methods=['POST'])
def analyze_text():
data = request.get_json()
text_from_user = data.get('text', '') # Use .get for safety
print(f"Received text: {text_from_user}")
# For now, just send a simple success message back
response_data = {
'status': 'success',
'received_text': text_from_user
}
return jsonify(response_data)
if __name__ == '__main__':
app.run(debug=True)
2. templates/index.html
Make sure you have a form with an id, a textarea, a button, and a <script> tag to link your JavaScript file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Writing Assistant</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
</head>
<body>
<main>
<h1>AI Writing Assistant</h1>
<form id="text-form">
<textarea id="user-text" placeholder="Enter your text here..."></textarea>
<button type="submit">Analyze Text</button>
</form>
</main>
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
</body>
</html>
3. static/js/script.js
Create a new folder named js inside your static folder, and inside js, create a file named script.js.
document.addEventListener('DOMContentLoaded', () => {
const textForm = document.getElementById('text-form');
textForm.addEventListener('submit', (event) => {
// Prevent the default form submission (page reload)
event.preventDefault();
// Get the text from the textarea
const userText = document.getElementById('user-text').value;
// Prepare the data to be sent
const data = { text: userText };
// Send the data to the Flask backend
fetch('/api/analyze', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
// Log the response from the server to the browser's console
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
});
});
How to Check If It Works
Now, run your app.py file and open your browser to http://127.0.0.1:5000. How can you tell if the client and server are talking?
- Check the Server Terminal: Type something in the text box and click "Analyze Text". Look at the terminal where you ran
python app.py. You should see theprint()message showing the text you entered. - Use Browser Dev Tools: In your browser, open the Developer Tools (usually by pressing F12 or right-clicking and selecting "Inspect"). Go to the Network tab. Now, click the "Analyze Text" button again. You should see a new entry appear named
analyze. Click on it. You can inspect its "Payload" to see the JSON data you sent.
This video gives a quick visual guide on using the Network tab.
POST Form Data as JSON with Fetch API in JavaScript
This video shows a great demonstration of how to use the browser's Network tab to inspect your API call.
Watch from 05:54 to 06:30. The author shows how to open the Network tab, find the request that was sent, and inspect its 'Payload' to see the JSON data that was sent to the server. This is a crucial skill for debugging.

Test your understanding!
Modify the analyze_text function in your app.py file. Instead of just returning the text you received, can you make it return the text converted to uppercase?
For example, if the user sends "hello world", the JSON response from the server should contain "HELLO WORLD".
Show answer
You would change the analyze_text function like this:
@app.route('/api/analyze', methods=['POST'])
def analyze_text():
data = request.get_json()
text_from_user = data.get('text', '')
# Convert the text to uppercase
processed_text = text_from_user.upper()
print(f"Received: '{text_from_user}', Sent back: '{processed_text}'")
response_data = {
'status': 'success',
'processed_text': processed_text # Send the new uppercase text
}
return jsonify(response_data)
After making this change, you'll need to stop and restart your Flask server. Now when you submit text, the console.log in your browser's console will show the uppercase version.
Conclusion
Fantastic work! This was a huge step. You've successfully built a communication channel between your user's browser and your Python backend.
Key Takeaways:
- You can prevent a form's default page-reloading behavior using
event.preventDefault()in JavaScript. - The
fetchAPI allows you to send HTTP requests from the browser to the server in the background. - To send data, you use the
POSTmethod and include your data in thebodyof the request. - Data sent over the network should be in a universal format like JSON. You use
JSON.stringify()in JavaScript to create it. - In Flask, you define an endpoint with
methods=['POST']and userequest.get_json()to receive and parse the data.
You have now built the most critical piece of infrastructure for our web application. In the next lesson, we will complete the loop. You'll learn how to take the JSON response from Flask and use it to dynamically update the webpage with the results, without ever needing a page refresh.