A real-time chat application built with raw WebSockets, custom authentication, and a full-stack architecture using Next.js, Express, PostgreSQL, Redis, Docker, and TanStack React Query.
This project was built to deeply understand:
- How WebSockets actually work (no Socket.io magic)
- How authentication works from scratch
- How sessions and caching behave in real systems
- How state syncing works between REST + WebSocket
- How real-time apps are structured in production
No Firebase. No third-party auth. No “chat SDK”.
Just:
- Raw WebSocket server
- Custom session-based auth (7-hour expiry)
- PostgreSQL for persistence
- Redis for caching
- In-memory tracking for live members
- TanStack React Query for client data synchronization
- Docker for environment isolation
Client (Next.js)
↓ REST (initial data)
Backend (Express)
↓ WebSocket (real-time events)
In-memory presence layer
↓
Redis (caching layer)
↓
PostgreSQL (source of truth)
- Next.js (App Router)
- TanStack React Query
- Zustand
- WebSocket API (native browser API)
- Express
- ws (raw WebSocket library)
- Prisma ORM
- PostgreSQL
- Redis
- Docker
- pnpm workspace
JWT+SESSION IN DB.
Sessions are stored in PostgreSQL and validated on:
- REST requests (via middleware)
- WebSocket connection handshake
- User signs in
- Session is created in DB
- Cookie stores session ID
- Middleware verifies session
- WebSocket validates session during connection
Sessions expire after 7 hours.
If expired:
- User is locked out
- Must re-authenticate
Built using ws.
- New message broadcasting
- Typing indicators
- Presence tracking
- Chat updates
- Member add/remove events
Online members are stored in memory (because presence is temporary state).
Why not database?
Because:
- Presence is ephemeral
- Writing to DB for every connect/disconnect = unnecessary load
- Memory is faster
This project was built mainly to understand how WebSockets work at a low level.
Goals included:
Handling WebSocket connections manually (no Socket.io)
Authenticating users during the socket handshake
Broadcasting events across connected clients
Managing in-memory presence
Syncing REST data with real-time updates
The focus was not on scaling or production hardening, but on understanding the mechanics of real-time communication from scratch.
Stores:
- Users
- Chats
- Messages
- Sessions
Used for:
- Frequently accessed chat data
- Reducing DB reads
Handles:
- Paginated chat messages
- Optimistic updates
- Cache invalidation
- Background refetching
WebSocket events update the React Query cache manually.
backend/
├── Express server
├── Prisma schema
├── REST routers
├── WebSocket server
├── Redis config
└── Auth middleware
frontend/
├── Next.js app router
├── WebSocket provider
├── React Query provider
├── Zustand stores
└── Feature-based structure
- User registration & login
- Session-based authentication (7-hour expiry)
- Create chats (private/group)
- Add / remove members(not implemented in fr)
- Rename chat (not implemented in fr)
- Transfer chat ownership(not implemented in fr)
- Close / reopen chats(not implemented in fr)
- Send messages
- Typing indicators
- Paginated message loading
- Optimistic UI updates
Because abstractions hide reality.
Using ws directly helped understand:
- Handshake lifecycle
- Connection state
- Event broadcasting
- Scaling limitations
- Memory tracking
- Authentication inside socket upgrade
No magic. Just protocol-level behavior.
- WebSockets are stateful scaling them is not trivial
- Presence tracking belongs in memory
- React Query + WebSocket is powerful but requires manual cache syncing
- Session-based auth is more controllable than JWT only for server-side systems
- Caching strategy matters more than people think
- Horizontal scaling with Redis pub/sub for WebSocket sync
- Rate limiting
- Message reactions
- Read receipts
- E2E encryption
- Load testing
- CI/CD pipeline
This is not a UI-heavy project.
This is a systems-understanding project.
It demonstrates:
- Real-time architecture knowledge
- Authentication design
- State synchronization
- Caching strategies
- Backend + frontend integration
If you're reading this as a recruiter or developer:
This project wasn’t built to make a complete chat app It was built to understand how chat systems actually work under