FastAPI WebSocket Endpoint: A Complete Guide
FastAPI WebSocket Endpoint: A Complete Guide
Hey guys! Ever wanted to build real-time features into your web applications? Think live chat, collaborative editing, or real-time dashboards? Well, you’re in luck! FastAPI and WebSockets are like peanut butter and jelly when it comes to creating these kinds of dynamic experiences. FastAPI’s simplicity and speed, combined with WebSockets’ ability to maintain a persistent connection between client and server, make for a powerful combo. In this guide, we’re diving deep into how to create WebSocket endpoints using FastAPI. We’ll start with the basics, then move on to more advanced topics, ensuring you’re well-equipped to build awesome real-time applications. So, buckle up and let’s get started!
Table of Contents
What are WebSockets?
Before we jump into the code, let’s quickly cover what WebSockets actually are . Unlike traditional HTTP requests where the client sends a request and the server sends a response, WebSockets provide a persistent , bidirectional communication channel. Imagine it like having a constant phone line open between the client and the server. This means data can be pushed from the server to the client without the client having to constantly ask for it. This is what makes real-time applications possible. Think of it this way: with regular HTTP, you have to keep refreshing the page to see updates. With WebSockets, the updates appear instantly as they happen!
WebSockets are based on the TCP protocol but introduce a handshake process to upgrade an HTTP connection to a WebSocket connection. This handshake ensures that both the client and server agree to use the WebSocket protocol for communication. Once the connection is established, data can be sent back and forth in frames . These frames are lightweight and efficient, making WebSockets ideal for real-time applications. Furthermore, WebSockets are designed to work well with existing web infrastructure, such as proxies and firewalls.
WebSockets are fantastic for scenarios where low latency and real-time updates are crucial. In addition to chat applications and collaborative tools, they’re also used in online gaming, financial applications (like stock tickers), and IoT (Internet of Things) devices. The ability to push data to clients without the overhead of constant HTTP requests makes WebSockets a game-changer for modern web development. So, understanding WebSockets is not just a nice-to-have; it’s becoming increasingly essential for building responsive and engaging applications. As you delve deeper into web development, you’ll find WebSockets to be an indispensable tool in your arsenal.
Setting up FastAPI for WebSockets
Okay, now that we’ve got the theory out of the way, let’s get our hands dirty with some code! First things first, you’ll need to have FastAPI installed. If you haven’t already, you can install it using pip. Also, you’ll need
uvicorn
, which is an ASGI server that FastAPI uses to run asynchronously. Just run this command in your terminal:
pip install fastapi uvicorn
Once you’ve got those installed, let’s create a basic FastAPI application. Create a file named
main.py
(or whatever you like) and add the following code:
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Message text was: {data}")
Let’s break down what’s happening here. We’re importing
FastAPI
and
WebSocket
from the
fastapi
library. Then, we create an instance of
FastAPI
. The
@app.websocket("/ws")
decorator is what defines our WebSocket endpoint. This tells FastAPI that any connections to the
/ws
path should be handled by the
websocket_endpoint
function. The
websocket_endpoint
function takes a
WebSocket
object as an argument. This object represents the WebSocket connection between the client and the server.
Inside the
websocket_endpoint
function, we first call
await websocket.accept()
to accept the incoming connection. This is a crucial step! Without it, the WebSocket connection won’t be established. Then, we enter an infinite loop that listens for incoming messages from the client using
await websocket.receive_text()
. When a message is received, we send a response back to the client using
await websocket.send_text()
. In this case, we’re simply echoing back the message we received, but you can do whatever you want here. This basic setup provides the foundation for building more complex real-time applications.
To run this application, save the
main.py
file and run the following command in your terminal:
uvicorn main:app --reload
The
--reload
flag tells Uvicorn to automatically reload the server whenever you make changes to the code, which is super handy for development. Now, your FastAPI application with a WebSocket endpoint is running! Next, we’ll look at how to connect to this endpoint from a client.
Connecting to the WebSocket Endpoint
Alright, the server is up and running, but it’s no good if we can’t connect to it! Let’s create a simple HTML page with some JavaScript to connect to our WebSocket endpoint. Create a file named
index.html
(or whatever you prefer) and add the following code:
<!DOCTYPE html>
<html>
<head>
<title>FastAPI WebSocket Client</title>
</head>
<body>
<h1>WebSocket Test</h1>
<input type="text" id="messageInput" placeholder="Type your message here">
<button onclick="sendMessage()">Send</button>
<div id="messages"></div>
<script>
const websocket = new WebSocket("ws://localhost:8000/ws");
websocket.onopen = () => {
console.log("Connected to WebSocket");
};
websocket.onmessage = (event) => {
const messages = document.getElementById("messages");
const message = document.createElement("p");
message.textContent = `Received: ${event.data}`;
messages.appendChild(message);
};
websocket.onclose = () => {
console.log("Disconnected from WebSocket");
};
websocket.onerror = (error) => {
console.error("WebSocket error:", error);
};
function sendMessage() {
const messageInput = document.getElementById("messageInput");
const message = messageInput.value;
websocket.send(message);
messageInput.value = "";
}
</script>
</body>
</html>
Let’s walk through this HTML and JavaScript code. We create a new
WebSocket
object, passing in the URL of our WebSocket endpoint (
ws://localhost:8000/ws
). Note the
ws://
protocol, which is used for unencrypted WebSocket connections. For secure connections, you’d use
wss://
. We then define several event handlers for the
WebSocket
object.
-
onopen: This is called when the WebSocket connection is successfully opened. We simply log a message to the console. -
onmessage: This is called when a message is received from the server. We create a new paragraph element, set its text content to the received message, and append it to themessagesdiv. -
onclose: This is called when the WebSocket connection is closed. We log another message to the console. -
onerror: This is called if there’s an error with the WebSocket connection. We log the error to the console.
The
sendMessage
function is called when the user clicks the “Send” button. It gets the message from the input field, sends it to the server using
websocket.send(message)
, and clears the input field. Now, open
index.html
in your browser. You should see a simple page with an input field and a button. Type a message and click “Send”. You should see the message echoed back to you in the
messages
div. Congratulations! You’ve successfully connected to your FastAPI WebSocket endpoint and sent a message!
Handling Multiple Clients
So far, we’ve only dealt with a single client. But what if we want to handle multiple clients simultaneously? No problem! FastAPI and WebSockets make it relatively easy. We’ll need to keep track of all the active WebSocket connections. Let’s modify our
main.py
file to handle multiple clients:
from fastapi import FastAPI, WebSocket
from typing import List
app = FastAPI()
active_connections: List[WebSocket] = []
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
active_connections.append(websocket)
try:
while True:
data = await websocket.receive_text()
for connection in active_connections:
await connection.send_text(f"Message text was: {data}")
except Exception as e:
print(f"Error: {e}")
finally:
active_connections.remove(websocket)
Here’s what we’ve changed: We import the
List
type from the
typing
module. We create a list called
active_connections
to store all the active WebSocket connections. When a new connection is established, we append it to the
active_connections
list. Inside the
while
loop, we iterate over all the active connections and send the received message to each one. This effectively broadcasts the message to all connected clients. We also add a
try...except...finally
block to handle exceptions and ensure that the connection is removed from the
active_connections
list when it’s closed.
With this modification, whenever a client sends a message, it will be broadcast to all other connected clients. This is the basic building block for creating real-time chat applications or collaborative editing tools. To test this, open multiple browser windows or tabs, each pointing to your
index.html
file. Send a message from one window, and you should see it appear in all the other windows. This demonstrates how FastAPI can easily handle multiple WebSocket connections concurrently.
Advanced WebSocket Features
Now that you’ve mastered the basics, let’s explore some more advanced WebSocket features that FastAPI offers. These features can help you build more robust and sophisticated real-time applications.
Sending and Receiving JSON
Instead of just sending plain text, you can also send and receive JSON data over WebSockets. This is useful for sending structured data between the client and server. To send JSON, use the
send_json
method. To receive JSON, use the
receive_json
method. Here’s an example:
from fastapi import FastAPI, WebSocket
import json
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
try:
data = await websocket.receive_json()
print(f"Received JSON: {data}")
await websocket.send_json({"message": "JSON received", "data": data})
except Exception as e:
print(f"Error: {e}")
break
On the client-side, you can use
JSON.stringify
to convert a JavaScript object to a JSON string before sending it, and
JSON.parse
to parse the received JSON string back into a JavaScript object. This makes it easy to work with complex data structures over WebSockets.
Handling Disconnections
It’s important to handle disconnections gracefully. When a client disconnects, you’ll want to remove it from the
active_connections
list and perform any necessary cleanup. The
finally
block in our previous example already handles removing the connection from the list. However, you might also want to log the disconnection or perform other actions. By properly handling disconnections, you can ensure that your application remains stable and reliable.
Authentication and Authorization
For many applications, you’ll need to authenticate and authorize WebSocket connections. FastAPI provides several ways to do this. One common approach is to use JWT (JSON Web Tokens). When a client connects to the WebSocket endpoint, it can send a JWT in the initial handshake. The server can then verify the JWT and authenticate the user. You can also use WebSocket subprotocols to negotiate authentication and authorization mechanisms.
Using Background Tasks
For long-running or CPU-intensive tasks, you can use FastAPI’s background tasks feature in conjunction with WebSockets. This allows you to offload tasks to a background process, preventing them from blocking the main event loop and ensuring that your WebSocket connections remain responsive. You can trigger background tasks when a message is received over the WebSocket, allowing you to perform asynchronous processing without affecting the real-time nature of the connection.
Conclusion
So, there you have it! A comprehensive guide to creating WebSocket endpoints with FastAPI. We’ve covered everything from the basics of setting up a WebSocket endpoint to handling multiple clients and exploring advanced features like sending JSON, handling disconnections, and implementing authentication. With this knowledge, you’re well on your way to building amazing real-time applications. Remember, practice makes perfect! So, experiment with these concepts, try building different types of real-time applications, and don’t be afraid to dive deeper into the FastAPI documentation. Happy coding, and enjoy the real-time revolution!