This workspace contains a basic full-stack starter with:
- Frontend: Next.js (App Router) + Tailwind CSS
- Backend: NestJS + JWT authentication + Socket.IO + Prisma/PostgreSQL
imcs/
├── frontend/
│ ├── app/
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── lib/
│ │ └── config.ts
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.js
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── backend/
│ ├── src/
│ │ ├── auth/
│ │ │ ├── dto/login.dto.ts
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.module.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── jwt-auth.guard.ts
│ │ │ └── jwt.strategy.ts
│ │ ├── events/
│ │ │ ├── events.gateway.ts
│ │ │ └── events.module.ts
│ │ ├── users/
│ │ │ ├── users.module.ts
│ │ │ └── users.service.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ └── main.ts
│ ├── .env.example
│ ├── nest-cli.json
│ ├── package.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
└── .gitignore
- Install dependencies:
cd frontend && npm install
cd ../backend && npm install- Configure environment:
cd backend
cp .env.example .env- Start PostgreSQL (required for message storage):
cd ..
docker compose up -d- Generate Prisma client and run migrations:
cd backend
npm run prisma:generate
npm run prisma:migrate:dev- Run apps in separate terminals:
cd backend && npm run start:dev
cd frontend && npm run devFrontend runs on http://localhost:3000 and backend on http://localhost:3001.
- Seed admin user is loaded from environment:
INIT_ADMIN_USERNAME(default:admin)INIT_ADMIN_PASSWORD(default:Admin123!)
POST /auth/loginwith{ "username": "admin", "password": "Admin123!" }- Returns
{ "access_token": "..." }and also sets anhttpOnlycookie. - Protected routes can use either:
Authorization: Bearer <token>- Auth cookie (sent automatically by browser)
POST /users(admin only)- Body:
{ "username": "new_user", "password": "StrongPass123", "role": "user" }
- Body:
GET /users(admin only)
GET /auth/profilerequires valid JWT via header or cookie.
- Socket server is attached to NestJS backend.
- Client can connect with JWT token using
auth: { token }.
join_room- payload:
{ roomKey: string, roomName?: string } - server emits:
room_joinedwith room metadata and message history.
- payload:
send_message- payload:
{ roomKey: string, content: string } - server emits:
receive_messageto all clients in the room.
- payload:
typing- payload:
{ roomKey: string, isTyping: boolean } - server forwards typing indicator to other room members.
- payload:
read_receipt- payload:
{ messageId: string, status: 'DELIVERED' | 'READ' } - server persists receipt and broadcasts the update.
- payload:
- Endpoint:
POST /files/upload(authenticated) - Request:
multipart/form-datawithfilefield - Backend uses Multer with local disk storage (
UPLOAD_DIR, defaultstorage/uploads) - Basic validation:
- Max size:
MAX_UPLOAD_SIZE_MB(default10) - Allowed types:
image/*,video/*,application/pdf
- Max size:
- Response includes public file URL under
/files/<filename>
Example response:
{
"fileName": "1710000000000-a1b2c3d4.png",
"originalName": "photo.png",
"mimeType": "image/png",
"size": 12345,
"url": "http://localhost:3001/files/1710000000000-a1b2c3d4.png"
}/login: sign in and create secure auth cookie./chat: room-based realtime group messaging UI with:- instant send/receive
- typing indicator
- delivered/read status labels
- upload button and inline file previews (image/pdf/video)