WebSocket Routing
When building real-time applications in FastAPI, you can organize your WebSocket endpoints using the APIRouter class. This allows you to group related WebSocket logic into separate files and apply shared dependencies or path prefixes across multiple connections.
Basic WebSocket Routing
To define a WebSocket endpoint on a router, use the @router.websocket() decorator. This decorator registers an APIWebSocketRoute which, unlike standard Starlette routes, supports FastAPI's dependency injection system.
from fastapi import APIRouter, WebSocket
router = APIRouter()
@router.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}")
To use this router in your main application, include it using app.include_router():
from fastapi import FastAPI
app = FastAPI()
app.include_router(router)
Injecting Dependencies
FastAPI's APIWebSocketRoute allows you to use Depends() in your WebSocket endpoints. Dependencies are resolved before the endpoint function is executed. If a dependency fails or raises an exception, the connection is handled according to the error type.
Parameter-level Dependencies
You can inject dependencies directly into the endpoint function parameters:
from fastapi import APIRouter, WebSocket, Depends
router = APIRouter()
async def get_cookie_or_token(websocket: WebSocket, session: str | None = None):
if session is None:
await websocket.close(code=1008)
return session
@router.websocket("/items")
async def read_items(
websocket: WebSocket,
session: str = Depends(get_cookie_or_token)
):
await websocket.accept()
await websocket.send_text(f"Session: {session}")
await websocket.close()
Router-level Dependencies
You can also apply dependencies to every WebSocket route in a router by passing them to the APIRouter constructor:
async def verify_auth():
# Shared logic for all routes in this router
pass
router = APIRouter(dependencies=[Depends(verify_auth)])
Structuring Large Applications
For larger projects, you can use path prefixes to group WebSocket endpoints. When you include a router with a prefix, FastAPI automatically prepends that prefix to all WebSocket paths defined within that router.
# chat.py
chat_router = APIRouter(prefix="/chat")
@chat_router.websocket("/room/{room_id}")
async def chat_room(websocket: WebSocket, room_id: str):
await websocket.accept()
await websocket.send_text(f"Welcome to room {room_id}")
# ... logic ...
In your main application file:
# main.py
from fastapi import FastAPI
from .chat import chat_router
app = FastAPI()
app.include_router(chat_router)
# The WebSocket is now available at ws://host:port/chat/room/{room_id}
Troubleshooting
Validation Errors
If a dependency uses Pydantic models or FastAPI's Query/Header parameters and the validation fails, FastAPI will automatically close the WebSocket connection with code 1008 (Policy Violation).
websocket() vs websocket_route()
APIRouter provides two methods for adding WebSocket routes:
.websocket(path): Recommended. Creates anAPIWebSocketRoutewhich supports FastAPI features like dependency injection..websocket_route(path): A lower-level method inherited from Starlette. It creates a standardrouting.WebSocketRoutewhich does not support FastAPI'sDepends()or automatic parameter resolution.
Path Prefixes
When using APIRouter(prefix="/something") or app.include_router(router, prefix="/something"), ensure the prefix starts with / and does not end with /. FastAPI's APIRouter.__init__ and include_router methods include assertions to enforce this formatting:
# This will raise an AssertionError
router = APIRouter(prefix="/api/")
# This is correct
router = APIRouter(prefix="/api")