A real-time communication backend built with Node.js, Express, Socket.io, PostgreSQL, and Redis.
- ✅ JWT-based authentication with access and refresh tokens
- ✅ User registration, login, and profile management
- ✅ Server creation and management with invite codes
- ✅ Text channel creation and organization
- ✅ Real-time messaging via WebSocket (Socket.io)
- ✅ Message history with cursor-based pagination
- ✅ Typing indicators
- ✅ User presence tracking (online/offline/idle/dnd)
- ✅ Permission-based access control
- ✅ Rate limiting and error handling
- ✅ Redis pub/sub for horizontal scaling
- Runtime: Node.js (latest stable)
- Framework: Express.js
- WebSocket: Socket.io
- Database: PostgreSQL
- ORM: Prisma
- Cache/PubSub: Redis
- Authentication: JWT
- Validation: Zod
- Logging: Pino
- Language: TypeScript (strict mode)
- Docker Desktop (includes Docker Compose)
- Node.js (v18 or higher)
- PostgreSQL (v14 or higher)
- Redis (v6 or higher)
- npm (v8 or higher)
The easiest way to run the entire stack locally:
1. Start all services:
docker-compose upThis will start:
- PostgreSQL database (port 5432)
- Redis (port 6379)
- Backend API (port 3000)
2. Access the application:
- HTTP API: http://localhost:3000
- WebSocket: ws://localhost:3000
- Health Check: http://localhost:3000/health
3. Useful Docker commands:
# Run in background
docker-compose up -d
# View logs
docker-compose logs -f backend
# Stop services
docker-compose down
# Stop and remove all data (fresh start)
docker-compose down -v
# Rebuild after code changes
docker-compose up --buildNote: The Docker setup automatically:
- Creates the database
- Runs migrations
- Hot-reloads on code changes in development mode
npm installCopy the example environment file and configure it:
cp .env.example .envEdit .env with your configuration:
NODE_ENV=development
PORT=3000
# PostgreSQL connection
DATABASE_URL=postgresql://username:password@localhost:5432/discord_clone
# Redis connection
REDIS_URL=redis://localhost:6379
# JWT secrets (generate strong random strings)
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
JWT_REFRESH_SECRET=your-super-secret-refresh-key-change-this-in-production
JWT_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
# CORS (your frontend URL)
CORS_ORIGIN=http://localhost:5173Create the PostgreSQL database:
createdb discord_cloneGenerate Prisma client and run migrations:
npm run prisma:generate
npm run prisma:migrateMake sure Redis is running:
# On Windows (if installed via Chocolatey or MSI)
redis-server
# On Linux/Mac
redis-serverDevelopment mode (with hot reload):
npm run devProduction mode:
npm run build
npm startThe server will start on http://localhost:3000 (or your configured PORT).
POST /api/v1/auth/register- Register new userPOST /api/v1/auth/login- Login userPOST /api/v1/auth/refresh- Refresh access token
GET /api/v1/users/me- Get current userGET /api/v1/users/:userId- Get user by IDGET /api/v1/users/username/:username- Get user by usernamePATCH /api/v1/users/me- Update profile
POST /api/v1/servers- Create serverGET /api/v1/servers- Get user's serversGET /api/v1/servers/:serverId- Get server detailsPATCH /api/v1/servers/:serverId- Update serverDELETE /api/v1/servers/:serverId- Delete serverPOST /api/v1/servers/join- Join server via invitePOST /api/v1/servers/:serverId/leave- Leave server
POST /api/v1/channels/servers/:serverId/channels- Create channelGET /api/v1/channels/servers/:serverId/channels- Get channelsGET /api/v1/channels/:channelId- Get channelPATCH /api/v1/channels/:channelId- Update channelDELETE /api/v1/channels/:channelId- Delete channel
POST /api/v1/messages/:channelId- Send messageGET /api/v1/messages/:channelId- Get messages (paginated)DELETE /api/v1/messages/:messageId- Delete message
channel:join- Join a channelchannel:leave- Leave a channelmessage:send- Send a messagetyping:start- Start typingtyping:stop- Stop typingpresence:status- Update status
message:new- New message in channeluser:joined- User joined channeluser:left- User left channeltyping:start- User started typingtyping:stop- User stopped typingpresence:update- User status changed
Connect with JWT token in handshake:
const socket = io('http://localhost:3000', {
auth: {
token: 'your-jwt-token'
}
});# Install dependencies
npm install
# Run in development mode
npm run dev
# Build for production
npm run build
# Start production server
npm start
# Generate Prisma client
npm run prisma:generate
# Run database migrations
npm run prisma:migrate
# Open Prisma Studio (database GUI)
npm run prisma:studio
# Lint code
npm run lint
# Format code
npm run formatbackend/
├── src/
│ ├── config/ # Environment & logger configuration
│ ├── modules/ # Feature modules
│ │ ├── auth/ # Authentication
│ │ ├── users/ # User management
│ │ ├── servers/ # Server management
│ │ ├── channels/ # Channel management
│ │ └── messages/ # Message management
│ ├── realtime/ # WebSocket gateway
│ ├── db/ # Prisma client
│ ├── redis/ # Redis client & pub/sub
│ ├── middleware/ # Express middleware
│ ├── utils/ # Utilities & helpers
│ ├── types/ # TypeScript types
│ ├── app.ts # Express app setup
│ ├── socket.ts # Socket.io setup
│ └── server.ts # Main entry point
├── prisma/
│ └── schema.prisma # Database schema
├── package.json
├── tsconfig.json
└── .env.example
- User - Authentication and profile
- Server - Community servers
- Channel - Text/voice channels
- Membership - User-server relationships with roles
- Message - Chat messages
- User registers/logs in → Receives access + refresh tokens
- Access token (15min) used for API requests
- Refresh token (7 days) used to get new access token
- Tokens sent in Authorization header:
Bearer <token>
- Redis pub/sub enables horizontal scaling of WebSocket servers
- Stateless HTTP endpoints can be load balanced
- PostgreSQL can be scaled with read replicas
- Messages use cursor-based pagination for performance
- Password hashing with bcrypt
- JWT token-based authentication
- Rate limiting on all endpoints
- CORS protection
- Request validation with Zod
- SQL injection protection via Prisma
- Custom roles and permissions
- Direct messaging (DMs)
- File uploads (images, attachments)
- Markdown support
- Message reactions
- User mentions
- Notifications
- Server categories
- Ensure PostgreSQL is running
- Verify DATABASE_URL in .env
- Check database exists:
psql -l
- Ensure Redis is running:
redis-cli ping - Verify REDIS_URL in .env
- Change PORT in .env
- Or kill process using the port
- Regenerate Prisma client:
npm run prisma:generate - Clear and reinstall:
rm -rf node_modules && npm install
ISC