A high-performance, real-time chat application built with a modern TypeScript stack. SocialIO features instant messaging, live presence indicators, typing indicators, image sharing, and deep WebSocket integration for a seamless user experience.
- Real-Time Messaging: Sub-millisecond message delivery via native WebSockets.
- Live Presence & Typing: "Active now" indicators, last-seen timestamps, and real-time typing indicators powered by Redis and Zustand.
- Optimistic UI: Messages render instantly on the client before server confirmation, ensuring a snappy feel.
- Media Sharing: Secure, signed image uploads directly to Cloudinary.
- Group Chats: Multi-user conversation support with admin roles and customizable group avatars.
- Message Actions: Edit messages inline, unsend (delete) messages, and add emoji reactions.
SocialIO is a monorepo managed by Turborepo. The frontend is a Next.js application that communicates with a Hono backend via both REST and WebSockets.
graph TD
Client[Next.js Client<br/>React Query + Zustand]
API[Hono API Gateway]
WS[Hono WS Server]
DB[(PostgreSQL)]
Cache[(Redis)]
CDN[Cloudinary]
Client <-->|REST API| API
Client <-->|WebSocket| WS
API <-->|Drizzle ORM| DB
WS <-->|Pub/Sub & Presence| Cache
API <-->|Session State & Read-Through Cache| Cache
Client -->|Signed Uploads| CDN
Redis operates as the central nervous system of SocialIO. For standard API requests, Hono attempts to fetch data from Redis first; if it misses, it falls back to PostgreSQL, populates the Redis cache, and returns the response. Redis also acts as the Pub/Sub broker for routing WebSocket messages across nodes and stores volatile session state.
Our real-time flow is designed to avoid unnecessary database reads. The WebSocket server directly mutates the frontend's Zustand store and TanStack Query cache.
sequenceDiagram
participant UserA as User A (Client)
participant WS_A as WS Server (Node A)
participant Redis as Redis PubSub
participant WS_B as WS Server (Node B)
participant UserB as User B (Client)
participant DB as PostgreSQL
UserA->>WS_A: 1. Send Message (HTTP POST)
WS_A->>DB: 2. Insert Message
WS_A->>Redis: 3. Publish 'new_message' event
Redis->>WS_B: 4. Route event to Node B
WS_B->>UserB: 5. Push 'new_message' via WS
UserB->>UserB: 6. Mutate Zustand/React Query Cache
- Frontend: Next.js 15, React 19, TailwindCSS, shadcn/ui, TanStack Query, Zustand, Framer Motion.
- Backend: Hono (Node.js Adapter), native
ws, Better-Auth. - Database: PostgreSQL, Drizzle ORM.
- Infrastructure: Redis (Pub/Sub & KV), Cloudinary (Media).
- Node.js (v20+)
- pnpm (v9+)
- PostgreSQL
- Redis
- Cloudinary Account
-
Clone the repository and install dependencies:
pnpm install
-
Environment Variables Setup:
- Copy
.env.exampleto.envinapps/server/and configure your Postgres, Redis, and Cloudinary keys. - Copy
.env.exampleto.envinapps/web/and set your public API URLs.
- Copy
-
Apply the database schema:
pnpm run db:push
-
Start the development environment:
pnpm run dev
The web application will be available at http://localhost:3001 and the API runs at http://localhost:3000.
SocialIO uses distinct workflows for local development and production.
We recommend running development servers natively on your machine while letting Docker handle the database and Redis infrastructure.
-
Start the Infrastructure Run PostgreSQL and Redis in the background:
turbo infra:start # or 'turbo infra:watch' to view logs -
Install Dependencies & Push Database Schema
pnpm install turbo db:push
-
Start the Apps Run the Next.js frontend and Hono backend with hot-reloading:
turbo dev
Run a minimal standalone production images utilizing Turborepo multi-stage pruning. The docker-compose.yml file builds isolated, non-root Alpine images for both web and server.
# Build the optimized images and start the environment
docker compose upThen visit http://localhost:3001 to see the production build in action.
Deploy minimal, standalone production images utilizing Turborepo multi-stage pruning. The docker-compose.yml file builds isolated, non-root Alpine images for both web and server.
# Build the optimized images and start the production environment
docker compose up --build -dsocialIO/
├── apps/
│ ├── web/ # Next.js frontend application
│ └── server/ # Hono backend API & WebSocket server
├── packages/
│ ├── ui/ # Shared shadcn/ui components & Tailwind config
│ ├── auth/ # Better-Auth configuration
│ ├── db/ # Drizzle ORM schema, migrations, & Redis client
│ └── env/ # Zod-validated environment variables
└── docs/ # Architecture diagrams and planning documentation
turbo dev: Start all applications in development mode.turbo dev:web: Start the Next.js frontend application.turbo dev:server: Start the Hono backend API & WebSocket server.turbo check-types: Check TypeScript types.turbo build: Build all workspace packages and apps.turbo db:start: Start a PostgreSQL database using Docker.turbo db:watch: Watch the PostgreSQL database.turbo db:stop: Stop the PostgreSQL database.turbo db:down: Down the PostgreSQL database.turbo db:generate: Generate Drizzle schema changes.turbo db:migrate: Run database migrations.turbo db:push: Push Drizzle schema changes to the PostgreSQL database.turbo db:studio: Launch Drizzle Studio to explore your database.turbo infra:start: Start the infrastructure services.turbo infra:stop: Stop the infrastructure services.turbo infra:down: Down the infrastructure services.turbo infra:watch: Watch the infrastructure services.turbo redis:ping: Ping the Redis server.turbo redis:cli:: Launch the Redis CLI.