Welcome to the first lesson of our course on developing a chatbot web application. In this lesson, we will focus on setting up a basic chat interface using FastAPI and HTML. This is an essential step in creating a user-friendly web application that enhances the user experience. A well-designed interface is crucial for engaging users and ensuring they can interact with the chatbot seamlessly.
FastAPI is primarily designed as an API framework, but it also provides excellent support for serving HTML content through template engines. Unlike traditional web frameworks that focus heavily on server-side rendering, FastAPI gives you the flexibility to choose how to handle HTML content.
Jinja2 is a modern and designer-friendly templating language for Python, inspired by Django's template system. It's fast, widely used, and features a sandboxed execution environment, making it secure for rendering user-provided templates. Jinja2 allows you to:
- Insert dynamic Python variables into HTML
- Use control structures like loops and conditionals
- Extend and include templates for reusable components
- Apply filters to modify variables during rendering
By combining FastAPI with Jinja2, we get the best of both worlds: FastAPI's performance and modern API capabilities alongside Jinja2's powerful templating system for HTML generation.
To serve HTML templates with FastAPI, we first need to create a dedicated templates
directory in our project. This directory will store all our HTML template files that will be rendered by the application.
1app/ 2│ 3├── main.py 4├── templates/ 5│ └── chat.html 6└── ...
The templates
directory is placed at the root level of our project, making it easily accessible from our main application file. This organization follows the conventional structure for web applications and keeps our template files separate from the application logic.
Once we have our directory structure in place, we need to configure FastAPI to use Jinja2 for template rendering. This is done by importing and initializing the Jinja2Templates
class in our main application file.
Python1from fastapi import FastAPI, Request 2from fastapi.responses import HTMLResponse 3from fastapi.templating import Jinja2Templates 4from starlette.middleware.sessions import SessionMiddleware 5from controllers.chat_controller import ChatController 6 7# Initialize FastAPI app 8app = FastAPI() 9 10# Add session middleware 11app.add_middleware( 12 SessionMiddleware, 13 secret_key="your_secret_key_here" 14) 15 16# Setup templates - points to the "templates" directory in our project 17templates = Jinja2Templates(directory="templates") 18 19# Create controller instance 20chat_controller = ChatController()
The Jinja2Templates
class is initialized with the path to our templates directory. This creates a template engine that FastAPI will use to render HTML files. The directory="templates"
parameter tells the engine where to look for template files.
With our templates directory and Jinja2 setup complete, we can now render HTML templates in our route handlers. This is done using the TemplateResponse
class, which combines a template with context data to produce an HTML response.
Python1@app.get("/", response_class=HTMLResponse) 2async def index(request: Request): 3 chat_controller.ensure_user_session(request.session) 4 # Render the "chat.html" template and pass the request object to it 5 return templates.TemplateResponse("chat.html", {"request": request})
In this route handler, we:
- Specify that the response should be HTML using
response_class=HTMLResponse
- Call
templates.TemplateResponse
to render our template - Pass the template name (
"chat.html"
) as the first argument - Provide a context dictionary as the second argument, which must include the
request
object
The TemplateResponse
will look for chat.html
in our templates directory, render it with the provided context, and return the resulting HTML to the client. This approach allows us to serve dynamic content while maintaining a clean separation between our application logic and presentation layer.
The HTML template for the chat interface begins with the basic structure of an HTML document. This includes the <!DOCTYPE html>
declaration, which defines the document type and version of HTML being used. The <html>
tag wraps the entire content of the page, and within it, the <head>
section is defined.
HTML, XML1<!DOCTYPE html> 2<html> 3<head> 4 <title>Customer Service Chat</title> 5</head> 6</html>
In the <head>
section, we set the title of the page to "Customer Service Chat", establishing the foundation for the chat interface.
Moving into the <body>
of the document, we start with a header section that sets the tone for the chat interface. This section is designed to welcome users and encourage them to engage with the chatbot.
HTML, XML1<!DOCTYPE html> 2<html> 3<!-- Head section... --> 4<body> 5 <div class="header"> 6 <h1>Welcome to Our Customer Service</h1> 7 <p>How can we help you today?</p> 8 </div> 9</body> 10</html>
The header includes a main heading (<h1>
) and a paragraph (<p>
), providing a friendly introduction to the chat service. This sets the stage for the interactive elements that follow.
Following the header, we define the chat container, which is the core of the user interface. This section is responsible for displaying the conversation and providing input elements for user interaction.
HTML, XML1<!DOCTYPE html> 2<html> 3<!-- Head section... --> 4<body> 5 <!-- Header section... --> 6 <div id="chat-container"> 7 <div id="messages"></div> 8 <div class="input-container"> 9 <div class="input-wrapper"> 10 <input type="text" id="message-input" placeholder="Type your message..."> 11 </div> 12 <button onclick="sendMessage()">Send</button> 13 <button id="new-chat-btn" onclick="startNewChat()">New Chat</button> 14 </div> 15 </div> 16</body> 17</html>
The #messages
div is where chat messages will appear, while the input field and buttons allow users to type and send messages. The "Send"
button triggers the sendMessage
function, and the "New Chat"
button clears the chat history, preparing the interface for a new conversation.
After setting up the HTML structure, we move on to adding interactivity to our chat interface using JavaScript. This is done by placing a script section at the bottom of the HTML document, where we'll define the necessary JavaScript functions.
HTML, XML1<!DOCTYPE html> 2<html> 3<!-- Head section... --> 4<body> 5 <!-- Header section... --> 6 <!-- Chat container and input elements... --> 7 <script> 8 // JavaScript functions will be defined here 9 </script> 10</body> 11</html>
In this section, we create a script block within our HTML code to define JavaScript functions that enable interactivity in the chat interface. By using plain JavaScript, we can directly manipulate HTML elements and handle user events. Placing the script at the end of the document ensures that all HTML elements are fully loaded before the script runs, preventing errors that might occur if the script tries to access elements that haven't been rendered yet. This approach allows us to seamlessly integrate JavaScript into our HTML, enhancing the functionality of our web application.
Before implementing the functions that handle chat interactions, it's important to obtain references to the necessary DOM elements. This allows us to manipulate these elements directly within our JavaScript code.
HTML, XML1<script> 2 // Get references to the messages container and message input field 3 const messagesContainer = document.getElementById('messages'); 4 const messageInput = document.getElementById('message-input'); 5</script>
By retrieving references to the messagesContainer
and messageInput
elements, we can easily update the chat interface and handle user input. The messagesContainer
is where chat messages will be displayed, and the messageInput
is the field where users type their messages. These references are crucial for implementing the interactive functions that follow.
With the necessary DOM elements initialized, we can proceed to create functions that enhance the interactivity of our chat interface. The startNewChat
function is designed to clear the chat history, allowing users to begin a fresh conversation. This function is triggered when the "New Chat" button is clicked.
HTML, XML1<script> 2 function startNewChat() { 3 // Clear the chat history 4 messagesContainer.innerHTML = ''; 5 } 6 7 // Start a chat automatically when the page loads 8 document.addEventListener('DOMContentLoaded', startNewChat); 9</script>
The startNewChat
function clears all messages from the chat interface, providing a clean slate for users to start a new conversation. This functionality is essential for resetting the chat and enhancing the user experience by allowing multiple interactions without refreshing the page.
Additionally, by adding an event listener for the DOMContentLoaded
event, we ensure that the startNewChat
function is automatically called when the page finishes loading. This means the chat interface is always initialized with a clean state, ready for user interaction as soon as the page is accessed. This approach enhances the user experience by ensuring the chat is ready to use immediately upon loading.
To effectively display messages in our chat interface, we use the appendMessage
function. This function creates a new message element, assigns it a CSS class based on the message's origin (user or assistant), appends it to the chat container, and ensures the chat view scrolls to the latest message.
HTML, XML1<script> 2 function appendMessage(role, content) { 3 // Create a new div element for the message 4 const messageDiv = document.createElement('div'); 5 6 // Assign a class to the message based on its role (user or assistant) 7 messageDiv.className = `message ${role}`; 8 9 // Set the text content of the message 10 messageDiv.textContent = content; 11 12 // Append the message to the messages container 13 messagesContainer.appendChild(messageDiv); 14 15 // Scroll the messages container to the bottom to show the latest message 16 messagesContainer.scrollTop = messagesContainer.scrollHeight; 17 } 18</script>
The appendMessage
function is crucial for dynamically adding messages to the chat interface. It creates a new <div>
element for each message, assigns a class to differentiate between user and assistant messages, and appends it to the messagesContainer
. This function also ensures that the chat view automatically scrolls to the bottom, keeping the latest messages in view.
Building on the appendMessage
function, the sendMessage
function handles user input and updates the chat interface. It processes the user's message, displays it, and simulates a response from the assistant. This function is triggered when the "Send" button is clicked or when the user presses Enter without holding Shift.
HTML, XML1<script> 2 function sendMessage() { 3 // Retrieve and trim the input value 4 const message = messageInput.value.trim(); 5 6 // If the message is empty, do not proceed 7 if (!message) return; 8 9 // Add user message to display 10 appendMessage('user', message); 11 12 // Clear the input field after sending the message 13 messageInput.value = ''; 14 15 // For now, just echo the message back 16 setTimeout(() => { 17 appendMessage('assistant', `You said: ${message}`); 18 }, 500); 19 } 20</script>
The sendMessage
function is responsible for capturing the user's input, ensuring it's not empty, and then displaying it in the chat interface using the appendMessage
function. After sending the message, it clears the input field to prepare for the next message. It also simulates a response from the assistant by echoing the user's message back after a short delay, demonstrating basic interactivity in the chat application.
To enhance user experience, we can allow users to send messages by pressing the Enter key. This functionality is implemented by listening for the Enter key press event on the input field.
HTML, XML1<script> 2 // Handle Enter key 3 messageInput.addEventListener('keypress', function(e) { 4 if (e.key === 'Enter' && !e.shiftKey) { 5 // Prevent the default form submission behavior 6 e.preventDefault(); 7 // Send the message when Enter key is pressed 8 sendMessage(); 9 } 10 }); 11</script>
This code snippet listens for the keypress
event on the messageInput
field. When the Enter key is pressed without the Shift key, it prevents the default behavior (which would be to insert a newline) and calls the sendMessage
function. This allows users to quickly send messages using the keyboard, improving the chat interface's usability.
In this lesson, we covered the essential steps for setting up a basic chat interface using FastAPI
and HTML. We explored how FastAPI
serves HTML templates using Jinja2
and how JavaScript is used to handle user interactions. By understanding the integration between FastAPI
, HTML, and JavaScript, you have laid the groundwork for building a dynamic web application. As you move on to the practice exercises, focus on reinforcing these concepts and experimenting with the code to deepen your understanding. This foundational knowledge will be crucial as we continue to enhance the chatbot's capabilities in future lessons.