Routers
Routers in SocketAPI allow you to organize your application into logical groups and split your code across multiple files. This is particularly useful for large applications where you want to separate concerns and maintain a clean project structure.
Basic Router Usage
Create a router and define channels and actions on it:
from socketapi import Router, SocketAPI
app = SocketAPI()
router = Router()
@router.channel("notifications")
async def notifications(message: str = "No new notifications"):
return {"message": message}
@router.action("mark_as_read")
async def mark_as_read(notification_id: int):
return {"id": notification_id, "read": True}
# Include the router in your app
app.include_router(router)
Organizing Multiple Files
For larger applications, split your routers into separate files:
Project Structure
my_app/
├── main.py
├── routers/
│ ├── __init__.py
│ ├── users.py
│ ├── chat.py
│ └── notifications.py
routers/users.py
from socketapi import Router
from pydantic import BaseModel
router = Router()
class User(BaseModel):
id: int
username: str
email: str
@router.channel("user_status")
async def user_status(user_id: int, status: str = "online"):
return {"user_id": user_id, "status": status}
@router.action("get_user")
async def get_user(user_id: int) -> User:
# Fetch user from database
return User(id=user_id, username="john_doe", email="john@example.com")
@router.action("update_status")
async def update_status(user_id: int, status: str):
# Update user status in database
await user_status(user_id=user_id, status=status)
return {"success": True}
routers/chat.py
from socketapi import Router
from pydantic import BaseModel
from typing import Annotated
from socketapi import RequiredOnSubscribe
router = Router()
class Message(BaseModel):
user: str
text: str
room: str
@router.channel("chat_room")
async def chat_room(
room_id: Annotated[str, RequiredOnSubscribe],
message: Message | None = None
):
if message:
return message
return {"room": room_id, "text": "Welcome to the chat room!"}
@router.action("send_message")
async def send_message(message: Message):
# Save message to database
await chat_room(room_id=message.room, message=message)
return {"sent": True}
@router.action("get_history")
async def get_history(room_id: str, limit: int = 50):
# Fetch chat history from database
return {"room": room_id, "messages": []}
routers/notifications.py
from socketapi import Router
from typing import Annotated
from socketapi import RequiredOnSubscribe
router = Router()
@router.channel("notifications", default_response=False)
async def notifications(
user_id: Annotated[int, RequiredOnSubscribe],
notification_type: str = "all"
):
return {
"user_id": user_id,
"type": notification_type,
"count": 0
}
@router.action("notify_user")
async def notify_user(user_id: int, message: str, notification_type: str = "info"):
# Send notification to user
await notifications(user_id=user_id, notification_type=notification_type)
return {"notified": True}
main.py
from socketapi import SocketAPI
from routers import users, chat, notifications
app = SocketAPI()
# Include all routers
app.include_router(users.router)
app.include_router(chat.router)
app.include_router(notifications.router)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Cross-Router Communication
Routers can interact with each other. You can call channel functions from different routers:
routers/analytics.py
from socketapi import Router
router = Router()
@router.channel("analytics")
async def analytics(event: str, data: dict):
return {"event": event, "data": data}
routers/orders.py
from socketapi import Router
from .analytics import analytics
router = Router()
@router.action("create_order")
async def create_order(product_id: int, quantity: int):
# Create order in database
order_id = 12345
# Track analytics event
await analytics(
event="order_created",
data={"order_id": order_id, "product_id": product_id, "quantity": quantity}
)
return {"order_id": order_id, "status": "created"}
Routers make no difference from the client's perspective - all channels and actions are available at the root level, regardless of which router they're defined in.