A full-stack, real-time chat app (Telegram/Discord-style):
- Custom Node.js backend (no Express) + Socket.IO
- JWT auth (cookies)
- PostgreSQL persistence
- Cloudinary for media uploads
- React + Vite front-end with Zustand, Tailwind/DaisyUI
Repo layout: backend/ and frontend/.
Frontend
- React (Vite), Tailwind CSS, DaisyUI
- Zustand, Axios
- Socket.IO client
Backend
- Node.js (custom HTTP server/router—no Express)
- Socket.IO
- JWT auth
- PostgreSQL (
pgpool) - Cloudinary SDK
- Cookie + CORS handling
- Custom middleware chain (parsing/routing/errors)
Dev/Tooling
- Node 18+ (20 LTS recommended)
- npm (or pnpm if you prefer—README shows npm where relevant)
- Node.js ≥ 18
- PostgreSQL ≥ 14
- Cloudinary account (for image uploads)
macOS (Apple Silicon, e.g., M3)
# Node (choose one approach you actually use)
brew install node
# PostgreSQL
brew install postgresql@14
brew services start postgresql@14Linux (Debian/Ubuntu)
# Node (example via NodeSource)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
sudo apt install -y postgresql postgresql-contrib
sudo systemctl enable --now postgresqlWindows
- Install Node LTS from nodejs.org (or use nvm-windows).
- Install PostgreSQL for Windows (pgAdmin includes
psql). - To run
.bashscripts use WSL (Ubuntu) or Git Bash.
Use your own names if you want; examples use messenger_user / messenger_db.
macOS/Linux
sudo -u postgres createuser -s messenger_user
createdb messenger_db -O messenger_user
psql -d messenger_db -c "CREATE EXTENSION IF NOT EXISTS pgcrypto;"Windows (in psql)
CREATE ROLE messenger_user WITH SUPERUSER LOGIN;
CREATE DATABASE messenger_db OWNER messenger_user;
\c messenger_db
CREATE EXTENSION IF NOT EXISTS pgcrypto;
\qCreate backend.env (put it wherever your backend reads it—root or backend/):
# Server
PORT=5000
CLIENT_ORIGIN=http://localhost:5173
# PostgreSQL (either DATABASE_URL or discrete vars)
DATABASE_URL=postgres://messenger_user@localhost:5432/messenger_db
# PGHOST=localhost
# PGPORT=5432
# PGUSER=messenger_user
# PGPASSWORD=
# PGDATABASE=messenger_db
# Auth
JWT_SECRET=change-this
# Cloudinary
CLOUDINARY_CLOUD_NAME=your_cloud
CLOUDINARY_API_KEY=your_key
CLOUDINARY_API_SECRET=your_secret
Create frontend/.env:
VITE_API_URL=http://localhost:5000
VITE_SOCKET_URL=http://localhost:5000
If using cookies: set Axios
withCredentials: trueand configure server CORS withAccess-Control-Allow-Credentials: trueand exactCLIENT_ORIGIN.
# Backend
cd backend
npm install
# Frontend
cd ../frontend
npm installYour scripts live under
./backend/scripts/. On macOS/Linux they run as is. On Windows, use WSL or Git Bash to execute them.
# macOS/Linux
chmod +x ./backend/scripts/setup_script.bash
./backend/scripts/setup_script.bash# Windows (WSL or Git Bash)
bash ./backend/scripts/setup_script.bash# macOS/Linux
chmod +x ./backend/scripts/starting_script.bash
./backend/scripts/starting_script.bash# Windows (WSL or Git Bash)
bash ./backend/scripts/starting_script.bashcd frontend
npm run dev # Vite at http://localhost:5173Open http://localhost:5173, create an account, and test:
- real-time messaging (Socket.IO)
- persistence (PostgreSQL)
- image uploads (Cloudinary)
-
CORS
Access-Control-Allow-Origin: http://localhost:5173Access-Control-Allow-Credentials: true- Allow the headers/methods your client uses.
-
Cookies/JWT
- Client must use
withCredentials: true - Verify JWT on protected HTTP routes and in Socket.IO handshake.
- Client must use
-
Windows
- Prefer WSL for the
.bashscripts. Ifpsqlisn’t on PATH, use “SQL Shell (psql)” or add PostgreSQLbinto PATH.
- Prefer WSL for the
backend/package.json
{
"scripts": {
"dev": "node --watch src/server.js",
"start": "node src/server.js"
}
}frontend/package.json
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}