Lesson 5
Building a RESTful API for Your Chatbot Service Using FastAPI
Serving Your Chatbot with a RESTful API Using FastAPI

Welcome to the next step in our journey of building a chatbot service with FastAPI. In the previous lesson, we focused on the ChatController, which manages chat sessions and handles messages by interacting with both the model and service layers. Now, we will take a significant step forward by creating a RESTful API for our chatbot service using FastAPI. We'll start by setting up the main FastAPI application, then adapt the ChatController to integrate with FastAPI's session management.

Understanding RESTful APIs and FastAPI

RESTful APIs are a way for different software systems to communicate over the internet. They provide a set of rules that allow programs to exchange data. FastAPI is a modern, fast (high-performance) web framework for building APIs with Python 3.7+ based on standard Python type hints. It is designed to be easy to use and to provide automatic interactive API documentation.

To get started with FastAPI, you can install it using pip by running the following command in your terminal or command prompt:

Bash
1pip install fastapi

Additionally, to run the FastAPI application, you'll need to install Uvicorn, an ASGI server:

Bash
1pip install uvicorn

Now, we can use FastAPI to connect the components we've already built, allowing users to interact with our chatbot service through a web interface. This will enable seamless communication between users and our chatbot service.

Initializing a FastAPI App

First, we need to initialize the FastAPI application:

Python
1from fastapi import FastAPI, Request 2 3# Initialize FastAPI app 4app = FastAPI())

Here, we import the FastAPI module and instantiate the FastAPI class to create our application object, app. This object will be used to configure and run our web application.

Adding Session Management with Starlette Middleware

FastAPI does not have built-in session management, but it's built on top of Starlette, which provides session handling capabilities. When you install FastAPI, Starlette comes bundled with it, giving you access to its middleware components.

A middleware is a function that works with every request before it's processed by any specific route handler. It sits between the server receiving the request and your route functions, allowing you to modify requests or responses globally.

We can add Starlette's SessionMiddleware to our FastAPI application to enable session management:

Python
1from starlette.middleware.sessions import SessionMiddleware 2 3# Add session middleware 4app.add_middleware( 5 SessionMiddleware, 6 secret_key="your_secret_key_here" 7)

The SessionMiddleware handles creating, reading, and updating session data stored in cookies. The secret_key parameter is crucial for securing session data, as it's used to sign the session cookies to prevent tampering.

Creating the Controller Instance

Next, we create an instance of our ChatController to handle the core operations of our chatbot service:

Python
1from controllers.chat_controller import ChatController 2 3# Create an instance of ChatController to handle chat operations 4chat_controller = ChatController()

This controller will manage chat sessions and process messages, serving as the bridge between our API endpoints and the underlying service layer. By instantiating it at the application level, we ensure it's available to all route handlers.

Ensuring a User Session with the Index Route

Now that we've initialized our FastAPI application and created our controller instance, let's create the API endpoints that will allow users to interact with our chatbot service. In FastAPI, routes are defined using decorators that specify the HTTP method and path.

Python
1# Define a route for the index page that ensures a user session 2@app.get("/") 3async def index(request: Request): 4 # Ensure user has a session 5 chat_controller.ensure_user_session(request.session) 6 return "Welcome to the Chatbot Service!"

This first route handles GET requests to the root path (/). The @app.get("/") decorator tells FastAPI that this function should be called when a user visits the homepage. The function receives a Request object, which contains information about the HTTP request, including the session data. We pass request.session to our controller to ensure the user has a session ID, then return a simple welcome message.

Creating New Chats with the Create Chat Route

Next, let's define a route for creating new chat sessions:

Python
1# Define a route for creating a new chat session 2@app.post("/api/create_chat") 3async def create_chat(request: Request): 4 # Handle chat creation request 5 return chat_controller.create_chat(request.session)

This route handles POST requests to /api/create_chat. We use POST instead of GET because we're creating a new resource (a chat session). Again, we pass request.session to our controller, which will use it to identify the user. The controller will return a JSON response containing the new chat ID, which FastAPI automatically converts to the appropriate HTTP response.

Handling Message Exchange with the Send Message Route

Finally, let's create a route for sending messages to the chatbot:

Python
1# Define a route for sending a message in an existing chat session 2@app.post("/api/send_message") 3async def send_message(request: Request): 4 # Parse JSON payload and handle message sending request 5 data = await request.json() 6 return chat_controller.send_message(request.session, data)

This route handles POST requests to /api/send_message. It's a bit more complex because we need to extract data from the request body. The line data = await request.json() parses the JSON payload sent by the client, which should contain the chat ID and the user's message. We then pass both the session and this data to our controller.

Notice that our controller will need to be adapted to work with these new parameters. Instead of directly receiving chat_id and user_message, it will receive the session object and a data dictionary. We'll explore these changes in the next section.

Running the FastAPI Server

Now that we've defined our routes and prepared our controller for integration with FastAPI, the final step is to set up the server to run our application. We'll use Uvicorn, an ASGI server, to serve our FastAPI application.

Python
1import uvicorn 2 3# Start the ASGI server to run the FastAPI application 4if __name__ == "__main__": 5 uvicorn.run( 6 "main:app", 7 host="0.0.0.0", 8 port=3000, 9 reload=True 10 )

This code block does several important things:

  1. The if __name__ == "__main__": condition ensures that the server only starts when we run this file directly, not when it's imported by another module.

  2. The uvicorn.run() function launches the ASGI server with our FastAPI application. The first argument, "main:app", tells Uvicorn where to find our application - in this case, the app variable in the main.py file. This syntax allows us to run the main file directly with a command like python main.py instead of having to use the uvicorn command-line interface.

  3. We set the host to "0.0.0.0" to make the server accessible from any network interface, not just localhost. This is useful if you want to access the server from other devices on your network.

  4. The port is set to 3000, so our application will be available at http://localhost:3000.

  5. The reload=True parameter enables hot reloading, which automatically restarts the server when you make changes to your code. This is extremely helpful during development.

However, before we can actually run our API and have it work correctly, we need to adapt our ChatController to work with FastAPI's session management and request handling. Our current controller implementation uses an internal test session and expects different parameters than what our routes are configured to provide. In the next section, we'll explore how to modify the ChatController to seamlessly integrate with our FastAPI routes.

Adapting the ChatController

To integrate the ChatController with FastAPI, we need to modify it to work with FastAPI's session management instead of our test session. Let's examine the key changes required for this integration.

Python
1import uuid 2from services.chat_service import ChatService 3 4class ChatController: 5 def __init__(self): 6 self.chat_service = ChatService() 7 # Removed: self.test_session = {}

The constructor has been simplified by removing the test_session dictionary that was previously used for testing purposes. Now, the controller will rely on FastAPI's built-in session management.

ChatController: Ensuring User Session

The ensure_user_session method now accepts a session parameter from FastAPI instead of using the internal test session:

Python
1def ensure_user_session(self, session): # Changed: Added session parameter 2 """Ensure user has a session ID.""" 3 if 'user_id' not in session: # Changed: Using passed session instead of self.test_session 4 session['user_id'] = str(uuid.uuid4()) # Changed: Storing in passed session 5 return session['user_id']

This method now works with FastAPI's session object, which is passed from the route handlers. It checks if a user ID exists in the session and creates one if needed, maintaining the same functionality but now integrated with FastAPI's session management system.

ChatController: Creating a Chat

The create_chat method has been updated to accept the session object from FastAPI:

Python
1def create_chat(self, session): # Changed: Added session parameter 2 """Handle chat creation request.""" 3 user_id = session.get('user_id') # Changed: Using passed session instead of self.test_session 4 if not user_id: 5 return {'error': 'Session expired'}, 401 6 7 chat_id = self.chat_service.create_chat(user_id) 8 return { 9 'chat_id': chat_id, 10 'message': 'Chat created successfully' 11 }

Instead of accessing the internal test session, this method now retrieves the user ID from the FastAPI session object. The rest of the logic remains the same - it creates a new chat using the ChatService and returns the chat ID along with a success message.

ChatController: Sending a Message

The send_message method has undergone the most significant changes to accommodate FastAPI's request handling:

Python
1def send_message(self, session, data): # Changed: Method signature now accepts session and data 2 """Handle message sending request.""" 3 user_id = session.get('user_id') # Changed: Using passed session instead of self.test_session 4 if not user_id: 5 return {'error': 'Session expired'}, 401 6 7 chat_id = data.get('chat_id') # Changed: Extracting chat_id from data object 8 user_message = data.get('message') # Changed: Extracting message from data object 9 10 if not chat_id or not user_message: 11 return {'error': 'Missing chat_id or message'}, 400 12 13 try: 14 ai_response = self.chat_service.process_message(user_id, chat_id, user_message) 15 return {'message': ai_response} 16 except ValueError as e: 17 return {'error': str(e)}, 404 18 except RuntimeError as e: 19 return {'error': str(e)}, 500

This method now accepts two parameters:

  1. The session object from FastAPI for retrieving the user ID
  2. The data object containing the request payload

Instead of receiving chat_id and user_message as direct parameters, it extracts them from the data object. This change accommodates FastAPI's JSON request handling, where the request body is parsed and passed to the controller. The error handling and core functionality remain consistent with the previous implementation.

How Clients Interact with the Chatbot API

To interact with our chatbot service, a client can follow these steps to send a message using the API:

  1. Access the Index Route (/): The client begins by accessing the index route of the API. This step ensures that a user session is established. A user session is a way to keep track of the user's interactions with the service. It is stored using FastAPI's session management, which utilizes cookies to maintain session data on the client side. The server responds with a welcome message, indicating that the service is ready for interaction.

  2. Create a Chat Session (/api/create_chat): Before sending a message, the client needs to create a new chat session. This is done by sending a request to the /api/create_chat route. The server will respond with a unique chat ID, which is essential for identifying the chat session in subsequent interactions. The user session ensures that the chat session is associated with the correct user, allowing for personalized interactions.

  3. Send a Message (/api/send_message): With the chat session established, the client can now send a message to the chatbot. This involves sending a request to the /api/send_message route, including the chat ID and the message content. The server processes the message and responds with the AI's reply, allowing the client to continue the conversation. The user session helps maintain continuity in the conversation by linking the messages to the correct user and chat session.

By following these steps, a client can effectively communicate with the chatbot service, leveraging the RESTful API to manage chat sessions and exchange messages while utilizing user sessions for a seamless experience.

Summary and Next Steps

In this lesson, we successfully built a RESTful API for our chatbot service using FastAPI. We set up a FastAPI application, defined routes for chat operations, and updated the ChatController to utilize FastAPI's session management. This lesson marks a significant milestone in our course, as it brings together all the components we've developed so far into a functional web application.

As you move on to the practice exercises, take the opportunity to experiment with the FastAPI API and reinforce the concepts covered in this lesson. This hands-on practice will prepare you for further development and exploration of additional FastAPI features and RESTful API concepts. Keep up the great work, and I look forward to seeing your progress!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.