JavaScript and Python Flask for AI Chat Applications

Implement JavaScript fetch to send user input as JSON to a Flask route and return a simulated AI response.

Build a dynamic chat interface by integrating JavaScript fetch method and Flask routes, setting the stage for real-time AI interactions. Discover how data travels seamlessly from user input to the server and back, emulating AI responses in your web application.

Key Insights

  • Utilize JavaScript's fetch method to send user-generated chat messages as JSON via POST requests to a Flask route, creating an interactive chat interface.
  • Establish a Flask route named "/chat" that accepts incoming JSON data through POST requests and responds with a simulated AI message formatted as JSON, setting a foundational step towards actual OpenAI API integration.
  • Implement client-side features such as dynamically creating chat bubbles, clearing input fields, and automatic scrolling to enhance user experience and emulate real-time conversational interactions.

Note: These materials offer prospective students a preview of how our classes are structured. Students enrolled in this course will receive access to the full set of materials, including video lectures, project-based assignments, and instructor feedback.

This is a lesson preview only. For the full lesson, purchase the course here.

Hi, welcome back to Python for AI apps with the OpenAI API and Flask, and many JavaScript. My name is Brian McLean. I hope you're enjoying the course so far. So in this lesson, we're gonna get into sending user chat messages from the webpage via the JavaScript fetch method to Flask.

Because in the previous lesson, we didn't send a message. We did have fetch hitting the route in Flask, but only to get a message sent back. We weren't sending anything over, which of course we'll need to do for the next step after that, which will be to send the user's chat message up to the OpenAI API and get the AI to answer us if we can. So again, this is Lesson 9. We're gonna send a message from the user. The user's gonna type—we're gonna go back to that Greenleaf site with the AI chat assistant, the little cute bunny, right? That website. The user's gonna type a message, click send, and the message will now be sent.

It's not gonna just get dumped out in the chat window, although it will still be outputted there. But then the message will be sent via fetch through a POST request over to our own route, which is not (parentheses), our API, right? Not cat fact—it's our route, our destination. Our web address, basically, which will be a route called /chat.

And then what will happen is, in Flask, we will just return a JSON answer, which we will call the AI answer—even though it won't really be the real AI answer—and then we'll output that. In the previous lesson, we already did that, right? We outputted the response from the Flask server. So let's dive into that in the book.

Lesson 9: JS fetch and Python Flask chat round trip. We're gonna use JS fetch to send chat data as JSON to Flask. Where will we get the chat data? It's going to be whatever the user types into that Greenleaf AI chat assistant window—that little input box.

Python for Data Science Bootcamp: Live & Hands-on, In NYC or Online, Learn From Experts, Free Retake, Small Class Sizes,  1-on-1 Bonus Training. Named a Top Bootcamp by Forbes, Fortune, & Time Out. Noble Desktop. Learn More.

We're gonna make a Flask route to receive the JSON data from JS fetch. We're gonna have the route return a message, also in JSON. JSON response: return JSON to emulate the AI response.

And then we're gonna output the faux AI response—the simulated AI response—to a chat bubble in the chat window. And then if you type another message, it'll repeat. So you'll see user chat bubble, AI chat bubble, user chat bubble, AI chat bubble, with the AI text coming from Flask. Except it'll just be the same AI again. It won't really be the AI. It'll just be that same static message coming in. But it is a necessary stepping stone between that and sending the user chat data to the OpenAI API for real and getting the real AI response.

So step one: we're gonna use JS fetch to send chat data as JSON to Flask. The JavaScript fetch method is for sending requests to a server URL, as we've seen. In this case, we will send the user's chat message together with the entire conversation to a Flask route called /chat.

The Greenleaf Tree Nursery website visitors chat to a user or the user, whether you're logged in or not, along with the entire conversation with the Flask route called /chat. Hopefully, that makes sense. And I could have said that about everything, right? We're gonna do a Save As of index 06—that's the latest Greenleaf. That's the Greenleaf Tree Nursery where we've got ourselves talking to ourselves, right? Where we're just having a one-sided conversation with ourselves.

We say, "Hi, " and it doesn't answer, and it doesn't. "Hola, " "Hello." It's just us talking to ourselves, right? We're gonna do a Save As of 06 as 09. So let's go to index 06, which is your Greenleaf.

There it is. File > Save As > index09. Excuse me.

Index09, and make sure you're importing script09. We have to do a script09. So do a Save As of script06—that's the Greenleaf script with the fetch in it.

Actually, it's not even doing fetch. It's just getting the user's input value and making the chat bubble. So let's—in any case—we're gonna build upon it.

We're gonna grab 06, do a Save As, and make it 09. In 09, we're gonna change the function to chat, since this is now gonna be a two-sided chat, albeit with a faux AI answer, which never changes, right? You're gonna be talking with an AI that says the same thing every time. So the function is gonna be called chat.

We change it in the listener and in the function, and it's still gonna be getting whatever the user types. It's still gonna be quitting out if the user clicked the send button without typing anything. It's still gonna make the user chat bubble and output that.

But now, fetch—gonna do a fetch to send the user message to the Flask route—is what we're gonna do now. Change a button to call the chat function, right? In script09, in the chat function at the end, add fetch chat at the end. Fetch chat followed by the POST method, which will receive—since the route—we're hitting a route that's gonna be receiving data via POST.

So fetch is gonna be receiving JSON, which you send via POST. You have to send JSON data via POST, which is not the same as GET. GET is when the data could be living in the URL, called a query string.

You cannot transmit JSON that way. It has to be handled invisibly through a method called POST. We're gonna be posting JSON to fetch, to Flask.

JSON—Flask is going to be receiving JSON from us now, not just text. And returning the same—because in a real chat with the AI, it's just JSON back and forth because of all these objects with the key-value pairs—right, role and content—all in squiggly, curly braces. Tons of that stuff. The conversation is JSON.

We use POST to send our share of the conversation over to Flask via fetch, and then Flask will return its side of the conversation—like the new AI response glued onto the entire conversation—back to us via POST as well. Or JSON—it'll come back to us as JSON, which we'll parse. Now we have this thing called a request body that's going back and forth.

We're going to fetch method POST. Fetch—our route is gonna be called /chat. We're gonna make that later in our new server.

And method—it's actually methods with an S—POST, because you can pass two methods there. You could say method—oh no, wait. Is it methods POST? No.

That methods POST is only the syntax over on the server-side. Here, it's method POST. Yeah, methods POST with the equal values over in the square brackets—that's over on the Flask side.

The route will have that on the Flask side. A little bit different syntax. Method with an—oh no, S. It's confusing because over on the other side in Flask, it is methods.

All right. Method POST. Now this object—we've used the object before with GET—but now we're going to actually put in more than just one property.

So after the method key-value pair, we're inside the same object. We're next going to put in headers, the value of which is content-type: application/json, which tells the destination Flask that what's coming in is JSON. So headers means it comes in right at the very beginning to tell the receiving end what's going on.

So it breaks out the right parsing library or the right parsing engine or however it reads this data. And that is an object, like so. Yeah.

Better be an object. Yeah, it's an object. How couldn't it be? Look, there's another key-value pair.

All right. It's not a good typo. Took out the catching typos.

It's quite a challenge to do a programming book at this level without any errors. I'm going through this for the nth time now. Now we have to pass in another key-value pair into our object that we're transmitting to Flask.

That being body, the value of which is a stringified or JSONified object with one property: userMessage, the value of which will be the user chat message—whatever the user typed into that box. So it's like over in Flask, they have jsonify; here, they have JSON.stringify. Here, that object is not in double quotes, whereas in Python, a dictionary—which is what you'd call this thing—has keys in double quotes.

We're going to use body: JSON.stringify(this object of one property). Let's say body JSON (all uppercase), stringify is the method. And we're stringifying an object of one key-value pair: userMessage (a key) with user chat message as the value.

Now the chat round trip. Next, we'll make the Python Flask script that runs the web server and receives a chat message from the JavaScript fetch method. So keeping it simple to start, we're going to skip the AI.

We're not going to send this message to the AI. We're just going to return a test message to complete the round trip from JS fetch to Python Flask and back. For review and practice, let us code this Python Flask server from scratch.

So in vs. Code, just make a new file. We're going to do this one from the top. Make a new file, server.

We don't want to make it in the JavaScript folder. That wouldn't do it. No, we don't. Server09, and server09 is not the file.

It's server09.py. You gotta type the file extension because it's a fresh file. In server09, we're going to import Flask, render_template, and jsonify. From Flask, import Flask, render_template, jsonify.

We're going to instantiate the Flask app. We're going to say app = Flask, and we're going to pass in that double underscore name variable, which we've talked all about in Lesson 1. We're going to define a route that runs when the browser hits the server home address, and that's going to render the index page, which is going to have the chat interface, the Greenleaf website.

We're going to say @app.route, and the route name, of course, is just going to be "/", right? A home route. And then we're going to say def index(), and that'll simply return render_template("index09.html"). And then we're going to define a chat route, and it's going to return JSON. It's going to return jsonify—this little message from the AI and key-value pairs from a Python dictionary.

The key has to be in quotes. It's just AI message. We're not even actually parsing the body yet of the incoming message.

We're just returning. In other words, no matter what comes in, it's not even looking at what's coming in. It's just hitting a route.

Right, so when this route is hit, the first thing it does is it does not check the incoming data, even though we're sending that. Right, we're just ignoring that fact for the moment. We just want to get that round trip happening—get this Flask route to say something back to the fetch so that we can make an AI bubble and put that out—a fake AI bubble.

We're going to return jsonify with our faux AI message. AI message is going to be "AI says: Let's chat, " or "AI: Let's chat." And then we end with `if __name__ == "__main__":`

Talked all about this in Lesson 1 as well. App.run(debug=True). And it's such a good idea to type all this from scratch.

Glad we did it. Handle the simulated AI chat message coming from the server. So the response that we just wrote—this return jsonify—is going to get boomeranged right back at you because we've got a request to that very route.

So the first `then` will parse the response JSON into a JS object, which will have a property of aiMessage, right? The server is coming back with this aiMessage property. The second `then` makes a chat bubble for the simulated AI message, which it then outputs to the chat window. Yep, that's going on over in the script file.

So in script09, let's chain the first `then` method onto the end of fetch. The first `then` will take the JSON response as its argument, parse it with the JSON method, and return a JS object. So continuing back up to your fetch—continuing from this body line—there's your little body stringify thing and fetch.

We're going to say `.then()`, and we'll just use shorter names here. `res.json()`—response JSON becomes the parsed object as a process of the first `then`. `.then()`—we'll call the incoming response JSON, crunch on it, and the outgoing will be response JSON parsed into an object.

Parse incoming JSON into a usable JS object. Output the simulated AI chat message to a chat bubble. We're going to invoke the second `then`, call the second `then`, which takes the parsed response object as input, and proceeds to make an AI chat bubble to hold the object's message.

We're going to—the next `then` is going to say—we could do this as one line. No, you can't. We have one line.

Okay, so what we're going to do is we're going to first make the chat bubble. Proceed to make the AI chat bubble. Handle the parsed message by putting it in a chat bubble.

We're going to say `.then()`. The response object is going to get all this handling. First, we need an AI chat bubble to put that message value in. `let aiChatBubble = document.createElement("div");` Then `aiChatBubble.className = "chat-bubble";`, which is the same for the user chat bubbles. But then we're going to differentiate the AI chat bubble from the user chat bubble by styling it.

We're going to go a little green here: `#EFE`. Then we're going to set `aiChatBubble.textContent = responseObj.aiMessage`. As they said in Silence of the Lambs, "It puts the message in the bubble."

Output bubble to chat window, right? Output AI chat bubble to chat window. We're going to say `chatWindow.appendChild(aiChatBubble);` That is the AI message.

Yep, there it is. Output the AI chat message to the bubble. It puts the message in the bubble.

Finishing the send user chat message—or just the chat function, actually. Finishing the chat function, chain `.catch()` onto the end of the fetch-then-then sequence. This will log an error if something goes wrong with fetch-then-then.

So you can actually not do this, but it's important. It's considered best practice by far. `catch`—it's going to take an input.

It's got a callback error, and we're going to return a little string that will CONCATENATE. We get an error. We'll just output an error message.

We gotta `console.log()` that though. Here we go. Then we're going to clear the input box to make way for the next user chat message.

Clear input box to make way for the next user chat message. `inputBox.value = "";` That's how you do that.

Oh, no, sorry. Set the value. It's not a div or anything—or a `

` tag, I'd say.

Of course, it is a form object, form element. It's got a value, not text content. Auto scroll to the bottom of the chat window.

This is a nifty little move because when a new message appears, you want to scroll down to the bottom of that thing automatically, right? You don't have to go fish for it. We're going to add this little auto-scroll setting. We're going to set the scrollTop of the chat window equal to its own height.

So it moves down. It moves the chat basically up the whole height of itself, making the bottom go to the top. We're making the very tail bottom go to the bottom of the chat window.

So you can see it. So you can see the latest thing at the bottom instead of having to scroll for it. `chatWindow.scrollTop = chatWindow.scrollHeight;`

Auto-scroll, end of chat bubbles. So that's going to be `chatWindow.scrollTop = chatWindow.scrollHeight;`. Now we're going to start the server and run the chat app. You're going to say, it's going to be 09 now.

Here we go. Let's ask a question. "How are you?" Okay, what's going on? Unexpected token, JS 29.

All right, what's going on? Line 29. Oh sure, you don't put a semicolon in the middle of your object.

No, you do not. You put a comma there—called a trailing comma. Let's see what happens.

"Hola, " all right. Oh of course—origin null—of course, because we're not viewing it on the server.

We're just looking at it the way it was before. We have to go to the server version of this. There we go.

"Hola." Okay, script09. Now it's still not happening.

  1. Method not allowed, chat 405. All right, why? 405, why, why, why? Oh, I know, I know, I know, I know.

It's because over in the server, in the chat route, that's where you have to set this `methods=["POST"]`, because even though we're not using it, we are sending data over. Also add `methods=["POST"]`, because the route is receiving a request body—if that makes any sense. Over in the fetch, it says POST.

We have to tell the route over here that it's coming in with POST. We can't just write it in the book. We have to actually write it in the code.

`methods`—and the reason it's `methods` with an S—is because you can put more than one method in there. You can put GET and POST. You could have this thing rendering or reacting to two different route hit styles.

And that's gonna probably fix it. Yep, "Let's chat"—it works, look. And then if you ask it another thing, it's just gonna say, "Let's chat" every time, because it's the same message coming back.

Let's handle the simulated—okay, so we handled the simulated message. We finished the chat function with the error. We auto-scrolled, we started the new server, and there you go.

Enter a chat message and click send. The simulated AI response should appear: "AI says: Let's chat."

You can keep chatting, but it will be a dull conversation as all the fake AI will ever say is, "Let's chat." There it is—put the newer one in there. Okay, the final code—HTML, JavaScript, methods, POST—there it is.

Return, yep. Okay, that's the end of Lesson 9. A one-way chat happening.

Now, you'll notice we did use this thing called the request body that we're sending over, but we haven't done anything with it. That's kind of just laying the groundwork a little bit for the next lesson. We're not gonna worry about that right now, but let's just look again real quick.

We're sending the chat message over. It's just over on the server. We don't look at it.

We don't look for it. We don't try to unpack it or anything. That's the next lesson.

But I just wanted to get a little head start on the idea of sending the data over from the chat, sending it back from Flask. The user chat message is sent over via fetch. The AI's response is sent back over using Flask.

All right, we're done with this lesson. We will see you in Lesson 10 coming right up, which will be completing the AI chat assistant. That's exciting.

Brian McClain

Brian is an experienced instructor, curriculum developer, and professional web developer, who in recent years has served as Director for a coding bootcamp in New York. Brian joined Noble Desktop in 2022 and is a lead instructor for HTML & CSS, JavaScript, and Python for Data Science. He also developed Noble's cutting-edge Python for AI course. Prior to that, he taught Python Data Science and Machine Learning as an Adjunct Professor of Computer Science at Westchester County College.

More articles by Brian McClain

How to Learn Python

Master Python with hands-on training. Python is a popular object-oriented programming language used for data science, machine learning, and web development. 

Yelp Facebook LinkedIn YouTube Twitter Instagram