personalOpen Source

LetsChat

A full-stack real-time messaging application built on an async FastAPI backend and a React 19 frontend. Messages, presence, and typing all flow over a single per-user WebSocket connection authenticated with a first-message token handshake so credentials never land in server access logs. The frontend pairs TanStack Query optimistic updates with a client-generated nonce so a sent message renders instantly and reconciles idempotently when the server echo arrives - no duplicates, no flicker, and a clear failed state on error. A WebSocket client singleton living outside React survives StrictMode remounts and reconnects with exponential backoff and heartbeat pings. Authentication is handled by fastapi-fullauth, my own open-source library, giving the app JWT access/refresh rotation for free. The backend is cleanly layered into repositories, services, and Pydantic schemas over async SQLAlchemy 2.0, with the chat list resolving each conversation's latest message in a single Postgres DISTINCT ON query and ordering by recent activity to avoid N+1 fan-out.

Key Highlights

  • Single per-user WebSocket endpoint carries messages, presence, and typing - authenticated with a first-message {type:'auth', token} handshake so the JWT stays out of connection access logs, with an app-defined 4401 close code on auth failure; an in-memory connection registry (dict[UUID, set[WebSocket]]) tracks every open socket per user and detects online/offline transitions on the first and last socket
  • Optimistic message delivery via TanStack Query - a client-generated nonce inserts the message instantly with a 'sending' state, then reconciles idempotently against the server's message.new broadcast by filtering on both id and nonce so the real row replaces the optimistic one with zero duplicates, and onError flips it to a visible 'failed' state
  • Live presence and typing indicators backed by Zustand stores with immutable Set updates for surgical re-renders - typing emits are throttled to one event every 2s and auto-expire after 3s of silence, presence shows online dots on avatars plus a header subtitle and an animated typing bubble in the conversation and the sidebar
  • A WebSocket client singleton instantiated outside the React tree survives StrictMode double-mounts, reconnects with exponential backoff capped at 30s, keeps the connection alive with 25s heartbeat pings, and fans events out to subscribers through a lightweight pub/sub so any component can listen without prop drilling
  • Layered async backend - repositories, services, and Pydantic v2 schemas over SQLAlchemy 2.0 and PostgreSQL; the chat list resolves each conversation's newest message in one Postgres DISTINCT ON query (no N+1) and sorts by most-recent activity, with message-send going through REST for a durable write that then triggers the WebSocket broadcast
  • React 19 frontend with the React Compiler, file-based TanStack Router (route loaders, pathless route groups, single-pane responsive layout via useMatchRoute), TanStack Query for server state, Tailwind CSS v4 and shadcn/ui, dark mode, skeleton loaders, and an error boundary
  • JWT access/refresh authentication delegated to fastapi-fullauth, my own PyPI library, plus user search and a block system; Dockerized local stack (PostgreSQL 17, Redis 8, API) with healthchecks, Alembic migrations, mypy --strict, and ruff linting