🌍 Languages: 🇬🇧 English | 🇷🇺 Русский
A real-time chat application built with FastAPI, WebSockets, PostgreSQL, Redis, and Celery.
This project demonstrates how to combine REST APIs, JWT authentication, WebSocket communication, background task processing, file sharing, and image processing into a modern chat backend.
- User registration
- Password hashing with bcrypt
- JWT-based authentication
- Protected routes
- View and update profile information
- Avatar uploads
- Automatic avatar resizing with Celery + Pillow
- Cached profile responses
- Create public and private chat rooms
- Join and leave rooms
- View room members
- Browse public rooms
- Real-time messaging with WebSockets
- Message history retrieval
- Cursor-based pagination
- Typing indicators
- Presence notifications
- Online user tracking
- Upload files to rooms
- Image thumbnail generation
- Real-time file sharing notifications
- Redis-backed Celery workers
- Avatar processing
- Thumbnail generation
- Scheduled message delivery (WIP)
| Layer | Technology |
|---|---|
| API Framework | FastAPI |
| Authentication | JWT (python-jose) |
| ORM | SQLAlchemy |
| Database Migrations | Alembic |
| Database | PostgreSQL |
| Cache | fastapi-cache2 |
| Message Broker | Redis |
| Task Queue | Celery |
| Image Processing | Pillow |
| Real-Time Communication | WebSockets |
| Containerization | Docker Compose |
| Testing | Pytest, HTTPX |
┌─────────────┐
│ Client │
└──────┬──────┘
│
▼
┌─────────────┐
│ FastAPI │
└──────┬──────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
PostgreSQL Redis Cache Celery Worker
│
▼
Image Processing
For real-time messaging:
Client A ◄────► FastAPI WebSocket ◄────► Client B
- Docker
- Docker Compose
- Python 3.12+
- PostgreSQL (if running without Docker)
Create a .env file:
DATABASE_URL=postgresql://chatapp_user:chatapp_pass@db:5432/chatapp
JWT_SECRET=your-secret-key
REDIS_URL=redis://redis:6379/0
UPLOAD_PATH=uploadsBuild and start:
docker compose up --buildSubsequent runs:
docker compose up -dRun migrations:
docker compose run --rm app uv run alembic upgrade headView worker logs:
docker compose logs -f workerRestart the Celery worker:
docker compose restart workerStop everything:
docker compose down -vpsql -U postgresCREATE DATABASE chatapp;
CREATE USER chatapp_user WITH PASSWORD 'chatapp_pass';
GRANT ALL PRIVILEGES ON DATABASE chatapp TO chatapp_user;
\qGrant schema permissions:
psql -U postgres -d chatapp -h localhostGRANT ALL ON SCHEMA public TO chatapp_user;
\qInitialize Alembic:
alembic init alembicGenerate migration:
alembic revision --autogenerate -m "initial"Apply migration:
alembic upgrade headPOST /auth/registerCreates a new user account.
POST /auth/loginReturns a JWT access token.
GET /users/mePATCH /users/mePOST /users/me/avatarPOST /roomsGET /roomsPOST /rooms/{id}/joinDELETE /rooms/{id}/leaveGET /rooms/{id}/membersGET /rooms/{id}/messagesSupports cursor pagination:
GET /rooms/{id}/messages?before=123POST /rooms/{id}/filesGET /rooms/{id}/filesConnect:
ws://localhost:8000/ws/rooms/{room_id}?token=<JWT>
{
"type": "message",
"content": "Hello world"
}{
"type": "typing"
}{
"type": "user_joined",
"user_id": 1
}{
"type": "user_left",
"user_id": 1
}{
"type": "file_shared",
"file_url": "...",
"filename": "image.png"
}Most chat workloads are I/O-bound rather than CPU-bound.
While users are connected, the server spends most of its time waiting for:
- Incoming messages
- Database responses
- Network events
Async endpoints and WebSockets allow thousands of concurrent connections without blocking worker threads.
Authentication flow:
Register
↓
Password Hashing
↓
Database Storage
↓
Login
↓
JWT Token
↓
Authenticated Requests
Benefits:
- Stateless
- Scalable
- REST-friendly
- Easy WebSocket authentication
Offset pagination:
OFFSET 50 LIMIT 50does not work well in chat systems because new messages constantly shift page boundaries.
Instead:
GET /messages?before=123anchors pagination to a specific message and prevents duplicates or missing messages.
| Feature | REST | WebSocket |
|---|---|---|
| Persistent Connection | ❌ | ✅ |
| Server Push | ❌ | ✅ |
| Real-Time Updates | ❌ | ✅ |
| Message Overhead | High | Low |
| Chat Applications | Poor Fit | Ideal |
REST is used for:
- Authentication
- Room management
- History retrieval
- File uploads
WebSockets are used for:
- Live messaging
- Presence notifications
- Typing indicators
- Real-time file sharing
After avatar upload:
process_avatar.delay(file_path)The worker:
- Receives the task from Redis
- Loads the image
- Resizes it to 256×256
- Saves the optimized version
When image files are uploaded:
generate_thumbnail.delay(file_path)Thumbnail generation happens asynchronously so uploads remain responsive.
Run tests:
docker compose exec app uv run pytest tests/test_auth.py -vManual WebSocket testing:
docker compose exec app uv run python tests/manual_ws_test.py --user 1
docker compose exec app uv run python tests/manual_ws_test.py --user 2Celery workers load task code into memory during startup.
When task implementations change:
docker compose restart workeris required so workers reload the updated code.
FastAPI runs with:
--reloadduring development, so API changes are automatically picked up.
- Scheduled messages
- Message editing
- Message deletion
- Read receipts
- Direct messages
- Push notifications
- Rate limiting
- End-to-end encryption
- Horizontal WebSocket scaling
MIT