How to create a full stack Chat App using Next js & Nest js?
Published: 2024-06-14 06:50:06

### Comprehensive Documentation for a Chat Application

#### Introduction
- **Purpose**: Outline the purpose of the document.
- **Scope**: Define the scope of the chat application.
- **Technologies Used**: List Next.js, NestJS, TailwindCSS, REST API, WebSocket, MongoDB.

#### Project Structure
- **Frontend**: Overview of the Next.js project structure.
- **Backend**: Overview of the NestJS project structure.
- **Database**: Structure of MongoDB collections (User, Chat, Message).

#### Sections and Functionality

### 1. User Authentication
- **User Registration**:
  - Endpoint: `/auth/register`
  - Method: `POST`
  - Payload: `{ "username": "string", "password": "string" }`
  - Description: Registers a new user.
  
- **User Login**:
  - Endpoint: `/auth/login`
  - Method: `POST`
  - Payload: `{ "username": "string", "password": "string" }`
  - Description: Authenticates a user and returns a JWT token.
  
- **User Logout**:
  - Description: Logout mechanism (typically handled on the client side by destroying the JWT token).

### 2. User Management
- **Profile Management**:
  - Endpoint: `/users/me`
  - Method: `GET`
  - Description: Fetches the logged-in user's profile.

- **Update Profile**:
  - Endpoint: `/users/me`
  - Method: `PUT`
  - Payload: `{ "username": "string", "password": "string" }`
  - Description: Updates the logged-in user's profile information.

### 3. Chat Management
- **Create Chat**:
  - Endpoint: `/chats`
  - Method: `POST`
  - Payload: `{ "participants": ["userId1", "userId2"] }`
  - Description: Creates a new chat session between users.

- **Fetch Chats**:
  - Endpoint: `/chats`
  - Method: `GET`
  - Description: Fetches all chat sessions for the logged-in user.

- **Fetch Chat Details**:
  - Endpoint: `/chats/:chatId`
  - Method: `GET`
  - Description: Fetches messages in a specific chat session.

### 4. Messaging
- **Send Message**:
  - Endpoint: `/chats/:chatId/messages`
  - Method: `POST`
  - Payload: `{ "content": "string" }`
  - Description: Sends a new message in a chat session.

- **Receive Messages**:
  - Description: Real-time message receiving using WebSocket.
  - WebSocket Event: `receiveMessage`

### 5. Real-time Communication
- **WebSocket Setup**:
  - Description: Initializing WebSocket connection on the client-side and handling events.
  
- **WebSocket Events**:
  - **sendMessage**: Event to send a message.
  - **receiveMessage**: Event to receive messages.

### 6. User Interface
- **Login Page**:
  - Description: UI for user login.
  - Components: Form, Input Fields, Submit Button.
  
- **Registration Page**:
  - Description: UI for user registration.
  - Components: Form, Input Fields, Submit Button.

- **Chat List Page**:
  - Description: UI for displaying the list of chat sessions.
  - Components: List of Chats, Search Bar.

- **Chat Window**:
  - Description: UI for displaying chat messages and sending new messages.
  - Components: Message List, Input Field, Send Button.

### 7. Notifications
- **Real-time Notifications**:
  - Description: Display real-time notifications for new messages.

### 8. File Sharing
- **Upload File**:
  - Endpoint: `/chats/:chatId/files`
  - Method: `POST`
  - Payload: `{ "file": "file object" }`
  - Description: Uploads a file to a chat session.

- **Download File**:
  - Endpoint: `/chats/:chatId/files/:fileId`
  - Method: `GET`
  - Description: Downloads a file from a chat session.

### 9. Settings
- **User Settings**:
  - Description: Page for user settings (e.g., notification preferences, account management).

### 10. Deployment
- **Frontend Deployment**:
  - Description: Steps to deploy the Next.js app using Vercel.
  
- **Backend Deployment**:
  - Description: Steps to deploy the NestJS app using Heroku, DigitalOcean, or AWS.
  
### 11. Security
- **JWT Authentication**:
  - Description: Implementing JWT for user authentication.
  
- **Data Encryption**:
  - Description: Encrypting sensitive data (e.g., passwords).

### 12. Testing
- **Unit Testing**:
  - Description: Writing unit tests for both frontend and backend.
  
- **Integration Testing**:
  - Description: Writing integration tests to test API endpoints.

### 13. Performance Optimization
- **Frontend Optimization**:
  - Description: Techniques for optimizing the Next.js application (e.g., code splitting, lazy loading).
  
- **Backend Optimization**:
  - Description: Techniques for optimizing the NestJS application (e.g., caching, database indexing).

### 14. Documentation
- **API Documentation**:
  - Description: Detailed documentation of all API endpoints using tools like Swagger.
  
- **User Guide**:
  - Description: Guide for end-users on how to use the chat application.

### 15. Future Enhancements
- **Voice and Video Calls**:
  - Description: Adding support for voice and video calls.
  
- **Group Chats**:
  - Description: Adding support for group chat functionality.
  
- **Status Indicators**:
  - Description: Adding online/offline status indicators for users.

This comprehensive breakdown covers the essential sections and functionalities needed to create a chat application similar to WhatsApp or Telegram using Next.js, NestJS, and TailwindCSS.

Creating a chat application like WhatsApp or Telegram involves a comprehensive set of features and technologies. Below is a detailed breakdown to help you get started with building a chat app using Next.js for the frontend, NestJS for the backend, TailwindCSS for styling, and REST APIs for communication.

### 1. Project Setup

#### Frontend (Next.js)
- **Initialize Next.js**: 
  ```bash
  npx create-next-app@latest chat-app
  cd chat-app
  ```
- **Install TailwindCSS**:
  ```bash
  npm install -D tailwindcss postcss autoprefixer
  npx tailwindcss init -p
  ```
- **Configure TailwindCSS**: Update `tailwind.config.js` and `globals.css`.
  ```js
  // tailwind.config.js
  module.exports = {
    content: [
      "./pages/**/*.{js,ts,jsx,tsx}",
      "./components/**/*.{js,ts,jsx,tsx}",
    ],
    theme: {
      extend: {},
    },
    plugins: [],
  };
  ```
  ```css
  /* globals.css */
  @tailwind base;
  @tailwind components;
  @tailwind utilities;
  ```

#### Backend (NestJS)
- **Initialize NestJS**:
  ```bash
  npm i -g @nestjs/cli
  nest new chat-backend
  cd chat-backend
  ```
- **Install Required Modules**:
  ```bash
  npm install @nestjs/mongoose mongoose @nestjs/passport passport passport-local bcryptjs
  npm install --save-dev @types/passport-local
  ```

### 2. Database Schema and Models
- **Define User and Chat Models**: Use Mongoose for schema definition.

#### User Schema
```typescript
import { Schema } from 'mongoose';

export const UserSchema = new Schema({
  username: { type: String, required: true, unique: true },
  password: { type: String, required: true },
});
```

#### Chat Schema
```typescript
import { Schema } from 'mongoose';

export const ChatSchema = new Schema({
  participants: [{ type: Schema.Types.ObjectId, ref: 'User' }],
  messages: [
    {
      sender: { type: Schema.Types.ObjectId, ref: 'User' },
      content: { type: String, required: true },
      timestamp: { type: Date, default: Date.now },
    },
  ],
});
```

### 3. Authentication
- **Local Strategy for Authentication**: Use Passport.js for authentication.
  
#### Local Strategy
```typescript
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super();
  }

  async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}
```

### 4. REST API Endpoints
- **User Registration and Login**: Implement endpoints for user registration and login.

#### Auth Controller
```typescript
import { Controller, Request, Post, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalAuthGuard } from './local-auth.guard';

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @Post('register')
  async register(@Request() req) {
    return this.authService.register(req.body);
  }

  @UseGuards(LocalAuthGuard)
  @Post('login')
  async login(@Request() req) {
    return this.authService.login(req.user);
  }
}
```

### 5. Chat Functionality
- **Create and Fetch Chats**: Implement endpoints for creating and fetching chats and messages.

#### Chat Controller
```typescript
import { Controller, Post, Get, Param, Body } from '@nestjs/common';
import { ChatService } from './chat.service';

@Controller('chats')
export class ChatController {
  constructor(private chatService: ChatService) {}

  @Post()
  async createChat(@Body() createChatDto: CreateChatDto) {
    return this.chatService.createChat(createChatDto);
  }

  @Get(':chatId')
  async getChat(@Param('chatId') chatId: string) {
    return this.chatService.getChat(chatId);
  }

  @Post(':chatId/messages')
  async sendMessage(@Param('chatId') chatId: string, @Body() sendMessageDto: SendMessageDto) {
    return this.chatService.sendMessage(chatId, sendMessageDto);
  }
}
```

### 6. Real-time Communication
- **WebSocket for Real-Time**: Integrate WebSocket for real-time messaging.

#### Install Dependencies
```bash
npm install @nestjs/websockets @nestjs/platform-socket.io
```

#### WebSocket Gateway
```typescript
import {
  SubscribeMessage,
  WebSocketGateway,
  OnGatewayInit,
  WebSocketServer,
  OnGatewayConnection,
  OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';

@WebSocketGateway()
export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer() server: Server;

  handleConnection(client: Socket, ...args: any[]) {
    console.log(`Client connected: ${client.id}`);
  }

  handleDisconnect(client: Socket) {
    console.log(`Client disconnected: ${client.id}`);
  }

  @SubscribeMessage('sendMessage')
  handleMessage(client: Socket, payload: any): void {
    this.server.emit('receiveMessage', payload);
  }
}
```

### 7. Frontend Integration
- **Real-Time Messaging with Socket.io**: Use Socket.io on the frontend for real-time updates.

#### Install Socket.io Client
```bash
npm install socket.io-client
```

#### Frontend Integration
```javascript
import { useEffect, useState } from 'react';
import io from 'socket.io-client';

const socket = io('http://localhost:3000');

export default function Chat() {
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState('');

  useEffect(() => {
    socket.on('receiveMessage', (message) => {
      setMessages((prevMessages) => [...prevMessages, message]);
    });
  }, []);

  const sendMessage = () => {
    socket.emit('sendMessage', input);
    setInput('');
  };

  return (
    <div className="chat-container">
      <div className="messages">
        {messages.map((msg, index) => (
          <div key={index}>{msg}</div>
        ))}
      </div>
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
        onKeyDown={(e) => (e.key === 'Enter' ? sendMessage() : null)}
      />
      <button onClick={sendMessage}>Send</button>
    </div>
  );
}
```

### 8. Styling with TailwindCSS
- **TailwindCSS for UI**: Style the chat interface using TailwindCSS.

#### Example Styles
```jsx
// Example chat component with TailwindCSS classes
export default function Chat() {
  // ... (useState and useEffect hooks)

  return (
    <div className="flex flex-col h-screen">
      <div className="flex-1 overflow-y-auto p-4">
        {messages.map((msg, index) => (
          <div key={index} className="bg-gray-200 p-2 my-2 rounded">
            {msg}
          </div>
        ))}
      </div>
      <div className="p-4 border-t border-gray-300 flex">
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          className="flex-1 p-2 border rounded"
          onKeyDown={(e) => (e.key === 'Enter' ? sendMessage() : null)}
        />
        <button
          onClick={sendMessage}
          className="ml-2 bg-blue-500 text-white p-2 rounded"
        >
          Send
        </button>
      </div>
    </div>
  );
}
```

### 9. Deployment and Hosting
- **Frontend Deployment**: Use Vercel for deploying the Next.js app.
- **Backend Deployment**: Use services like Heroku, DigitalOcean, or AWS for deploying the NestJS app.

By following this breakdown, you can build a comprehensive chat application with real-time messaging capabilities, user authentication, and a responsive UI. Adjust and expand upon these basics to include additional features like user profiles, file sharing, notifications, etc., as needed.

### User Authentication: User Registration

#### Backend Code (NestJS)

1. **Auth Module Setup**:
   - **Generate the Auth Module**:
     ```bash
     nest generate module auth
     nest generate service auth
     nest generate controller auth
     ```

2. **User Schema**:
   - **Create User Schema** (`src/schemas/user.schema.ts`):
     ```typescript
     import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
     import { Document } from 'mongoose';

     export type UserDocument = User & Document;

     @Schema()
     export class User {
       @Prop({ required: true, unique: true })
       username: string;

       @Prop({ required: true })
       password: string;
     }

     export const UserSchema = SchemaFactory.createForClass(User);
     ```

3. **User DTO (Data Transfer Object)**:
   - **Create DTO for User Registration** (`src/auth/dto/register-user.dto.ts`):
     ```typescript
     export class RegisterUserDto {
       username: string;
       password: string;
     }
     ```

4. **Auth Service**:
   - **Update the Auth Service** (`src/auth/auth.service.ts`):
     ```typescript
     import { Injectable } from '@nestjs/common';
     import { InjectModel } from '@nestjs/mongoose';
     import { Model } from 'mongoose';
     import { User, UserDocument } from '../schemas/user.schema';
     import { RegisterUserDto } from './dto/register-user.dto';
     import * as bcrypt from 'bcrypt';

     @Injectable()
     export class AuthService {
       constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {}

       async register(registerUserDto: RegisterUserDto): Promise<User> {
         const { username, password } = registerUserDto;

         // Check if the user already exists
         const existingUser = await this.userModel.findOne({ username }).exec();
         if (existingUser) {
           throw new Error('User already exists');
         }

         // Hash the password
         const salt = await bcrypt.genSalt();
         const hashedPassword = await bcrypt.hash(password, salt);

         // Create a new user
         const newUser = new this.userModel({ username, password: hashedPassword });
         return newUser.save();
       }
     }
     ```

5. **Auth Controller**:
   - **Update the Auth Controller** (`src/auth/auth.controller.ts`):
     ```typescript
     import { Controller, Post, Body } from '@nestjs/common';
     import { AuthService } from './auth.service';
     import { RegisterUserDto } from './dto/register-user.dto';
     import { User } from '../schemas/user.schema';

     @Controller('auth')
     export class AuthController {
       constructor(private readonly authService: AuthService) {}

       @Post('register')
       async register(@Body() registerUserDto: RegisterUserDto): Promise<User> {
         return this.authService.register(registerUserDto);
       }
     }
     ```

6. **Mongoose Setup**:
   - **Configure Mongoose in App Module** (`src/app.module.ts`):
     ```typescript
     import { Module } from '@nestjs/common';
     import { MongooseModule } from '@nestjs/mongoose';
     import { AuthModule } from './auth/auth.module';
     import { User, UserSchema } from './schemas/user.schema';

     @Module({
       imports: [
         MongooseModule.forRoot('mongodb://localhost/chat-app'),
         MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
         AuthModule,
       ],
     })
     export class AppModule {}
     ```

### Frontend Code (Next.js)

1. **API Call for Registration**:
   - **Create API Function** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000/auth';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };
     ```

2. **Registration Form**:
   - **Create Registration Form Component** (`src/components/RegisterForm.js`):
     ```javascript
     import { useState } from 'react';
     import { register } from '../services/api';

     export default function RegisterForm() {
       const [username, setUsername] = useState('');
       const [password, setPassword] = useState('');
       const [message, setMessage] = useState('');

       const handleSubmit = async (e) => {
         e.preventDefault();
         try {
           const data = await register(username, password);
           setMessage('User registered successfully');
         } catch (error) {
           setMessage(`Error: ${error.message}`);
         }
       };

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Register</h2>
           <form onSubmit={handleSubmit}>
             <div className="mb-4">
               <label className="block text-gray-700">Username</label>
               <input
                 type="text"
                 value={username}
                 onChange={(e) => setUsername(e.target.value)}
                 className="w-full px-3 py-2 border rounded"
               />
             </div>
             <div className="mb-4">
               <label className="block text-gray-700">Password</label>
               <input
                 type="password"
                 value={password}
                 onChange={(e) => setPassword(e.target.value)}
                 className="w-full px-3 py-2 border rounded"
               />
             </div>
             <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
               Register
             </button>
           </form>
           {message && <p className="mt-4">{message}</p>}
         </div>
       );
     }
     ```

### Conclusion
This setup includes the backend implementation for user registration with NestJS and a simple frontend registration form with Next.js. It uses MongoDB for data storage and bcrypt for password hashing. Adjust and expand this as needed for your complete chat application.

### User Authentication: User Login and Logout

#### Backend Code (NestJS)

1. **Auth Module Setup** (continued from the previous setup)

2. **Login DTO (Data Transfer Object)**:
   - **Create DTO for User Login** (`src/auth/dto/login-user.dto.ts`):
     ```typescript
     export class LoginUserDto {
       username: string;
       password: string;
     }
     ```

3. **JWT Module Setup**:
   - **Install JWT Package**:
     ```bash
     npm install @nestjs/jwt passport-jwt
     npm install --save-dev @types/passport-jwt
     ```
   - **Configure JWT in Auth Module** (`src/auth/auth.module.ts`):
     ```typescript
     import { Module } from '@nestjs/common';
     import { JwtModule } from '@nestjs/jwt';
     import { PassportModule } from '@nestjs/passport';
     import { AuthService } from './auth.service';
     import { AuthController } from './auth.controller';
     import { User, UserSchema } from '../schemas/user.schema';
     import { MongooseModule } from '@nestjs/mongoose';
     import { JwtStrategy } from './jwt.strategy';

     @Module({
       imports: [
         MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
         PassportModule,
         JwtModule.register({
           secret: 'YOUR_SECRET_KEY', // Replace with a secure key
           signOptions: { expiresIn: '1h' },
         }),
       ],
       providers: [AuthService, JwtStrategy],
       controllers: [AuthController],
     })
     export class AuthModule {}
     ```

4. **Auth Service (continued)**:
   - **Update Auth Service to Handle Login and JWT Generation** (`src/auth/auth.service.ts`):
     ```typescript
     import { Injectable } from '@nestjs/common';
     import { InjectModel } from '@nestjs/mongoose';
     import { Model } from 'mongoose';
     import { JwtService } from '@nestjs/jwt';
     import { User, UserDocument } from '../schemas/user.schema';
     import { RegisterUserDto } from './dto/register-user.dto';
     import { LoginUserDto } from './dto/login-user.dto';
     import * as bcrypt from 'bcrypt';

     @Injectable()
     export class AuthService {
       constructor(
         @InjectModel(User.name) private userModel: Model<UserDocument>,
         private jwtService: JwtService,
       ) {}

       async register(registerUserDto: RegisterUserDto): Promise<User> {
         const { username, password } = registerUserDto;

         const existingUser = await this.userModel.findOne({ username }).exec();
         if (existingUser) {
           throw new Error('User already exists');
         }

         const salt = await bcrypt.genSalt();
         const hashedPassword = await bcrypt.hash(password, salt);

         const newUser = new this.userModel({ username, password: hashedPassword });
         return newUser.save();
       }

       async validateUser(username: string, password: string): Promise<User> {
         const user = await this.userModel.findOne({ username }).exec();
         if (!user) {
           return null;
         }

         const isPasswordValid = await bcrypt.compare(password, user.password);
         if (!isPasswordValid) {
           return null;
         }

         return user;
       }

       async login(loginUserDto: LoginUserDto): Promise<{ access_token: string }> {
         const { username, password } = loginUserDto;
         const user = await this.validateUser(username, password);
         if (!user) {
           throw new Error('Invalid credentials');
         }

         const payload = { username: user.username, sub: user._id };
         return {
           access_token: this.jwtService.sign(payload),
         };
       }
     }
     ```

5. **Auth Controller (continued)**:
   - **Update Auth Controller for Login** (`src/auth/auth.controller.ts`):
     ```typescript
     import { Controller, Post, Body } from '@nestjs/common';
     import { AuthService } from './auth.service';
     import { RegisterUserDto } from './dto/register-user.dto';
     import { LoginUserDto } from './dto/login-user.dto';
     import { User } from '../schemas/user.schema';

     @Controller('auth')
     export class AuthController {
       constructor(private readonly authService: AuthService) {}

       @Post('register')
       async register(@Body() registerUserDto: RegisterUserDto): Promise<User> {
         return this.authService.register(registerUserDto);
       }

       @Post('login')
       async login(@Body() loginUserDto: LoginUserDto): Promise<{ access_token: string }> {
         return this.authService.login(loginUserDto);
       }
     }
     ```

6. **JWT Strategy**:
   - **Create JWT Strategy for Protecting Routes** (`src/auth/jwt.strategy.ts`):
     ```typescript
     import { Strategy, ExtractJwt } from 'passport-jwt';
     import { PassportStrategy } from '@nestjs/passport';
     import { Injectable } from '@nestjs/common';
     import { JwtPayload } from './jwt-payload.interface';
     import { AuthService } from './auth.service';

     @Injectable()
     export class JwtStrategy extends PassportStrategy(Strategy) {
       constructor(private authService: AuthService) {
         super({
           jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
           ignoreExpiration: false,
           secretOrKey: 'YOUR_SECRET_KEY', // Replace with a secure key
         });
       }

       async validate(payload: JwtPayload) {
         return { userId: payload.sub, username: payload.username };
       }
     }
     ```

7. **JWT Payload Interface**:
   - **Create JWT Payload Interface** (`src/auth/jwt-payload.interface.ts`):
     ```typescript
     export interface JwtPayload {
       username: string;
       sub: string;
     }
     ```

### Frontend Code (Next.js)

1. **API Call for Login**:
   - **Update API Function** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000/auth';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const login = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/login`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };
     ```

2. **Login Form**:
   - **Create Login Form Component** (`src/components/LoginForm.js`):
     ```javascript
     import { useState } from 'react';
     import { login } from '../services/api';

     export default function LoginForm() {
       const [username, setUsername] = useState('');
       const [password, setPassword] = useState('');
       const [message, setMessage] = useState('');

       const handleSubmit = async (e) => {
         e.preventDefault();
         try {
           const data = await login(username, password);
           localStorage.setItem('token', data.access_token);
           setMessage('User logged in successfully');
         } catch (error) {
           setMessage(`Error: ${error.message}`);
         }
       };

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Login</h2>
           <form onSubmit={handleSubmit}>
             <div className="mb-4">
               <label className="block text-gray-700">Username</label>
               <input
                 type="text"
                 value={username}
                 onChange={(e) => setUsername(e.target.value)}
                 className="w-full px-3 py-2 border rounded"
               />
             </div>
             <div className="mb-4">
               <label className="block text-gray-700">Password</label>
               <input
                 type="password"
                 value={password}
                 onChange={(e) => setPassword(e.target.value)}
                 className="w-full px-3 py-2 border rounded"
               />
             </div>
             <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
               Login
             </button>
           </form>
           {message && <p className="mt-4">{message}</p>}
         </div>
       );
     }
     ```

3. **Logout Function**:
   - **Handle Logout on the Client Side**:
     ```javascript
     export const logout = () => {
       localStorage.removeItem('token');
       window.location.href = '/login'; // Redirect to login page
     };
     ```

   - **Example Logout Button**:
     ```javascript
     import { logout } from '../services/api';

     export default function LogoutButton() {
       return (
         <button onClick={logout} className="bg-red-500 text-white px-4 py-2 rounded">
           Logout
         </button>
       );
     }
     ```

### Conclusion

This setup includes the backend implementation for user login with NestJS and a simple frontend login form with Next.js. The logout mechanism is handled on the client side by removing the JWT token from local storage. This setup provides the basic authentication flow for a chat application.

### User Management: Profile Management

#### Backend Code (NestJS)

1. **User Profile Endpoint**

2. **Create User Profile DTO**:
   - **Create DTO for User Profile** (`src/auth/dto/user-profile.dto.ts`):
     ```typescript
     export class UserProfileDto {
       username: string;
     }
     ```

3. **User Service**:
   - **Update User Service to Handle Profile Fetching** (`src/auth/auth.service.ts`):
     ```typescript
     import { Injectable } from '@nestjs/common';
     import { InjectModel } from '@nestjs/mongoose';
     import { Model } from 'mongoose';
     import { JwtService } from '@nestjs/jwt';
     import { User, UserDocument } from '../schemas/user.schema';
     import { RegisterUserDto } from './dto/register-user.dto';
     import { LoginUserDto } from './dto/login-user.dto';
     import { UserProfileDto } from './dto/user-profile.dto';
     import * as bcrypt from 'bcrypt';

     @Injectable()
     export class AuthService {
       constructor(
         @InjectModel(User.name) private userModel: Model<UserDocument>,
         private jwtService: JwtService,
       ) {}

       // ... other methods

       async getUserProfile(userId: string): Promise<UserProfileDto> {
         const user = await this.userModel.findById(userId).exec();
         if (!user) {
           throw new Error('User not found');
         }
         return { username: user.username };
       }
     }
     ```

4. **Auth Controller**:
   - **Update Auth Controller to Include Profile Fetching** (`src/auth/auth.controller.ts`):
     ```typescript
     import { Controller, Post, Get, Request, Body, UseGuards } from '@nestjs/common';
     import { AuthService } from './auth.service';
     import { RegisterUserDto } from './dto/register-user.dto';
     import { LoginUserDto } from './dto/login-user.dto';
     import { User } from '../schemas/user.schema';
     import { JwtAuthGuard } from './jwt-auth.guard';

     @Controller('auth')
     export class AuthController {
       constructor(private readonly authService: AuthService) {}

       @Post('register')
       async register(@Body() registerUserDto: RegisterUserDto): Promise<User> {
         return this.authService.register(registerUserDto);
       }

       @Post('login')
       async login(@Body() loginUserDto: LoginUserDto): Promise<{ access_token: string }> {
         return this.authService.login(loginUserDto);
       }

       @UseGuards(JwtAuthGuard)
       @Get('me')
       async getProfile(@Request() req): Promise<{ username: string }> {
         return this.authService.getUserProfile(req.user.userId);
       }
     }
     ```

5. **JWT Auth Guard**:
   - **Create JWT Auth Guard** (`src/auth/jwt-auth.guard.ts`):
     ```typescript
     import { Injectable } from '@nestjs/common';
     import { AuthGuard } from '@nestjs/passport';

     @Injectable()
     export class JwtAuthGuard extends AuthGuard('jwt') {}
     ```

6. **Update JWT Strategy**:
   - **Ensure Payload Includes User ID** (`src/auth/jwt.strategy.ts`):
     ```typescript
     import { Strategy, ExtractJwt } from 'passport-jwt';
     import { PassportStrategy } from '@nestjs/passport';
     import { Injectable } from '@nestjs/common';
     import { JwtPayload } from './jwt-payload.interface';
     import { AuthService } from './auth.service';

     @Injectable()
     export class JwtStrategy extends PassportStrategy(Strategy) {
       constructor(private authService: AuthService) {
         super({
           jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
           ignoreExpiration: false,
           secretOrKey: 'YOUR_SECRET_KEY', // Replace with a secure key
         });
       }

       async validate(payload: JwtPayload) {
         return { userId: payload.sub, username: payload.username };
       }
     }
     ```

### Frontend Code (Next.js)

1. **API Call for Fetching User Profile**:
   - **Create API Function for Fetching User Profile** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000/auth';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const login = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/login`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getProfile = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/me`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const logout = () => {
       localStorage.removeItem('token');
       window.location.href = '/login'; // Redirect to login page
     };
     ```

2. **Profile Page**:
   - **Create Profile Page Component** (`src/pages/profile.js`):
     ```javascript
     import { useEffect, useState } from 'react';
     import { getProfile } from '../services/api';

     export default function Profile() {
       const [profile, setProfile] = useState(null);
       const [error, setError] = useState('');

       useEffect(() => {
         const fetchProfile = async () => {
           try {
             const token = localStorage.getItem('token');
             if (token) {
               const data = await getProfile(token);
               setProfile(data);
             } else {
               setError('No token found');
             }
           } catch (err) {
             setError(err.message);
           }
         };

         fetchProfile();
       }, []);

       if (error) {
         return <div className="text-red-500">{error}</div>;
       }

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Profile</h2>
           {profile ? (
             <div>
               <p><strong>Username:</strong> {profile.username}</p>
             </div>
           ) : (
             <p>Loading...</p>
           )}
         </div>
       );
     }
     ```

### Conclusion

This setup includes the backend implementation for fetching the logged-in user's profile with NestJS and a simple frontend profile page with Next.js. The profile endpoint is protected with JWT authentication, ensuring only authenticated users can access their profile information.

### User Management: Update Profile

#### Backend Code (NestJS)

1. **Update User Profile DTO**:
   - **Create DTO for Updating User Profile** (`src/auth/dto/update-user.dto.ts`):
     ```typescript
     export class UpdateUserDto {
       username?: string;
       password?: string;
     }
     ```

2. **User Service**:
   - **Update User Service to Handle Profile Updating** (`src/auth/auth.service.ts`):
     ```typescript
     import { Injectable } from '@nestjs/common';
     import { InjectModel } from '@nestjs/mongoose';
     import { Model } from 'mongoose';
     import { JwtService } from '@nestjs/jwt';
     import { User, UserDocument } from '../schemas/user.schema';
     import { RegisterUserDto } from './dto/register-user.dto';
     import { LoginUserDto } from './dto/login-user.dto';
     import { UpdateUserDto } from './dto/update-user.dto';
     import * as bcrypt from 'bcrypt';

     @Injectable()
     export class AuthService {
       constructor(
         @InjectModel(User.name) private userModel: Model<UserDocument>,
         private jwtService: JwtService,
       ) {}

       // ... other methods

       async updateUserProfile(userId: string, updateUserDto: UpdateUserDto): Promise<User> {
         const user = await this.userModel.findById(userId).exec();
         if (!user) {
           throw new Error('User not found');
         }

         if (updateUserDto.username) {
           user.username = updateUserDto.username;
         }

         if (updateUserDto.password) {
           const salt = await bcrypt.genSalt();
           user.password = await bcrypt.hash(updateUserDto.password, salt);
         }

         return user.save();
       }
     }
     ```

3. **Auth Controller**:
   - **Update Auth Controller to Include Profile Updating** (`src/auth/auth.controller.ts`):
     ```typescript
     import { Controller, Post, Get, Put, Request, Body, UseGuards } from '@nestjs/common';
     import { AuthService } from './auth.service';
     import { RegisterUserDto } from './dto/register-user.dto';
     import { LoginUserDto } from './dto/login-user.dto';
     import { UpdateUserDto } from './dto/update-user.dto';
     import { User } from '../schemas/user.schema';
     import { JwtAuthGuard } from './jwt-auth.guard';

     @Controller('auth')
     export class AuthController {
       constructor(private readonly authService: AuthService) {}

       @Post('register')
       async register(@Body() registerUserDto: RegisterUserDto): Promise<User> {
         return this.authService.register(registerUserDto);
       }

       @Post('login')
       async login(@Body() loginUserDto: LoginUserDto): Promise<{ access_token: string }> {
         return this.authService.login(loginUserDto);
       }

       @UseGuards(JwtAuthGuard)
       @Get('me')
       async getProfile(@Request() req): Promise<{ username: string }> {
         return this.authService.getUserProfile(req.user.userId);
       }

       @UseGuards(JwtAuthGuard)
       @Put('me')
       async updateProfile(@Request() req, @Body() updateUserDto: UpdateUserDto): Promise<User> {
         return this.authService.updateUserProfile(req.user.userId, updateUserDto);
       }
     }
     ```

4. **JWT Auth Guard and Strategy** (from previous steps).

### Frontend Code (Next.js)

1. **API Call for Updating User Profile**:
   - **Create API Function for Updating User Profile** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000/auth';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const login = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/login`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getProfile = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/me`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const updateProfile = async (token, userData) => {
       try {
         const response = await axios.put(`${API_URL}/me`, userData, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const logout = () => {
       localStorage.removeItem('token');
       window.location.href = '/login'; // Redirect to login page
     };
     ```

2. **Update Profile Form**:
   - **Create Update Profile Form Component** (`src/components/UpdateProfileForm.js`):
     ```javascript
     import { useState, useEffect } from 'react';
     import { getProfile, updateProfile } from '../services/api';

     export default function UpdateProfileForm() {
       const [username, setUsername] = useState('');
       const [password, setPassword] = useState('');
       const [message, setMessage] = useState('');

       useEffect(() => {
         const fetchProfile = async () => {
           try {
             const token = localStorage.getItem('token');
             if (token) {
               const data = await getProfile(token);
               setUsername(data.username);
             }
           } catch (err) {
             setMessage(`Error: ${err.message}`);
           }
         };

         fetchProfile();
       }, []);

       const handleSubmit = async (e) => {
         e.preventDefault();
         try {
           const token = localStorage.getItem('token');
           const userData = { username, password: password || undefined };
           const data = await updateProfile(token, userData);
           setMessage('Profile updated successfully');
         } catch (error) {
           setMessage(`Error: ${error.message}`);
         }
       };

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Update Profile</h2>
           <form onSubmit={handleSubmit}>
             <div className="mb-4">
               <label className="block text-gray-700">Username</label>
               <input
                 type="text"
                 value={username}
                 onChange={(e) => setUsername(e.target.value)}
                 className="w-full px-3 py-2 border rounded"
               />
             </div>
             <div className="mb-4">
               <label className="block text-gray-700">Password</label>
               <input
                 type="password"
                 value={password}
                 onChange={(e) => setPassword(e.target.value)}
                 className="w-full px-3 py-2 border rounded"
                 placeholder="Leave blank to keep the same"
               />
             </div>
             <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
               Update
             </button>
           </form>
           {message && <p className="mt-4">{message}</p>}
         </div>
       );
     }
     ```

### Conclusion

This setup includes the backend implementation for updating the logged-in user's profile with NestJS and a simple frontend update profile form with Next.js. The profile update endpoint is protected with JWT authentication, ensuring only authenticated users can update their profile information.

### Chat Management: Create Chat

#### Backend Code (NestJS)

1. **Chat Module Setup**
   - **Generate the Chat Module**:
     ```bash
     nest generate module chat
     nest generate service chat
     nest generate controller chat
     ```

2. **Chat Schema**:
   - **Create Chat Schema** (`src/schemas/chat.schema.ts`):
     ```typescript
     import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
     import { Document, Types } from 'mongoose';

     export type ChatDocument = Chat & Document;

     @Schema()
     export class Chat {
       @Prop({ type: [{ type: Types.ObjectId, ref: 'User' }], required: true })
       participants: Types.ObjectId[];

       @Prop({ type: [{ type: Object }] })
       messages: { sender: Types.ObjectId; content: string; timestamp: Date }[];
     }

     export const ChatSchema = SchemaFactory.createForClass(Chat);
     ```

3. **Create Chat DTO**:
   - **Create DTO for Creating Chat** (`src/chat/dto/create-chat.dto.ts`):
     ```typescript
     export class CreateChatDto {
       participants: string[];
     }
     ```

4. **Chat Service**:
   - **Update Chat Service to Handle Chat Creation** (`src/chat/chat.service.ts`):
     ```typescript
     import { Injectable } from '@nestjs/common';
     import { InjectModel } from '@nestjs/mongoose';
     import { Model } from 'mongoose';
     import { Chat, ChatDocument } from '../schemas/chat.schema';
     import { CreateChatDto } from './dto/create-chat.dto';

     @Injectable()
     export class ChatService {
       constructor(@InjectModel(Chat.name) private chatModel: Model<ChatDocument>) {}

       async createChat(createChatDto: CreateChatDto): Promise<Chat> {
         const newChat = new this.chatModel({
           participants: createChatDto.participants,
           messages: [],
         });
         return newChat.save();
       }
     }
     ```

5. **Chat Controller**:
   - **Update Chat Controller to Include Chat Creation** (`src/chat/chat.controller.ts`):
     ```typescript
     import { Controller, Post, Body, UseGuards } from '@nestjs/common';
     import { ChatService } from './chat.service';
     import { CreateChatDto } from './dto/create-chat.dto';
     import { JwtAuthGuard } from '../auth/jwt-auth.guard';
     import { Chat } from '../schemas/chat.schema';

     @Controller('chats')
     export class ChatController {
       constructor(private readonly chatService: ChatService) {}

       @UseGuards(JwtAuthGuard)
       @Post()
       async createChat(@Body() createChatDto: CreateChatDto): Promise<Chat> {
         return this.chatService.createChat(createChatDto);
       }
     }
     ```

6. **JWT Auth Guard** (from previous steps).

### Frontend Code (Next.js)

1. **API Call for Creating Chat**:
   - **Create API Function for Creating Chat** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const login = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/login`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getProfile = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/auth/me`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const updateProfile = async (token, userData) => {
       try {
         const response = await axios.put(`${API_URL}/auth/me`, userData, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const createChat = async (token, participants) => {
       try {
         const response = await axios.post(`${API_URL}/chats`, { participants }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const logout = () => {
       localStorage.removeItem('token');
       window.location.href = '/login'; // Redirect to login page
     };
     ```

2. **Create Chat Form**:
   - **Create Chat Form Component** (`src/components/CreateChatForm.js`):
     ```javascript
     import { useState } from 'react';
     import { createChat } from '../services/api';

     export default function CreateChatForm() {
       const [participants, setParticipants] = useState('');
       const [message, setMessage] = useState('');

       const handleSubmit = async (e) => {
         e.preventDefault();
         try {
           const token = localStorage.getItem('token');
           const participantIds = participants.split(',').map(id => id.trim());
           const data = await createChat(token, participantIds);
           setMessage('Chat created successfully');
         } catch (error) {
           setMessage(`Error: ${error.message}`);
         }
       };

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Create Chat</h2>
           <form onSubmit={handleSubmit}>
             <div className="mb-4">
               <label className="block text-gray-700">Participants</label>
               <input
                 type="text"
                 value={participants}
                 onChange={(e) => setParticipants(e.target.value)}
                 className="w-full px-3 py-2 border rounded"
                 placeholder="Comma-separated user IDs"
               />
             </div>
             <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
               Create Chat
             </button>
           </form>
           {message && <p className="mt-4">{message}</p>}
         </div>
       );
     }
     ```

### Conclusion

This setup includes the backend implementation for creating a new chat session with NestJS and a simple frontend form for creating a chat with Next.js. The chat creation endpoint is protected with JWT authentication, ensuring only authenticated users can create new chat sessions.

### Chat Management: Fetch Chats

#### Backend Code (NestJS)

1. **Chat Service**:
   - **Update Chat Service to Handle Fetching Chats** (`src/chat/chat.service.ts`):
     ```typescript
     import { Injectable } from '@nestjs/common';
     import { InjectModel } from '@nestjs/mongoose';
     import { Model } from 'mongoose';
     import { Chat, ChatDocument } from '../schemas/chat.schema';
     import { User, UserDocument } from '../schemas/user.schema';

     @Injectable()
     export class ChatService {
       constructor(
         @InjectModel(Chat.name) private chatModel: Model<ChatDocument>,
         @InjectModel(User.name) private userModel: Model<UserDocument>,
       ) {}

       async createChat(participants: string[]): Promise<Chat> {
         const newChat = new this.chatModel({
           participants: participants,
           messages: [],
         });
         return newChat.save();
       }

       async getChats(userId: string): Promise<Chat[]> {
         return this.chatModel.find({ participants: userId }).populate('participants', 'username').exec();
       }
     }
     ```

2. **Chat Controller**:
   - **Update Chat Controller to Include Fetching Chats** (`src/chat/chat.controller.ts`):
     ```typescript
     import { Controller, Post, Get, Body, UseGuards, Request } from '@nestjs/common';
     import { ChatService } from './chat.service';
     import { CreateChatDto } from './dto/create-chat.dto';
     import { JwtAuthGuard } from '../auth/jwt-auth.guard';
     import { Chat } from '../schemas/chat.schema';

     @Controller('chats')
     export class ChatController {
       constructor(private readonly chatService: ChatService) {}

       @UseGuards(JwtAuthGuard)
       @Post()
       async createChat(@Body() createChatDto: CreateChatDto): Promise<Chat> {
         return this.chatService.createChat(createChatDto.participants);
       }

       @UseGuards(JwtAuthGuard)
       @Get()
       async getChats(@Request() req): Promise<Chat[]> {
         return this.chatService.getChats(req.user.userId);
       }
     }
     ```

3. **Ensure JWT Auth Guard is Applied**:
   - Ensure that `JwtAuthGuard` is properly set up and imported as shown in previous examples.

### Frontend Code (Next.js)

1. **API Call for Fetching Chats**:
   - **Create API Function for Fetching Chats** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const login = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/login`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getProfile = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/auth/me`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const updateProfile = async (token, userData) => {
       try {
         const response = await axios.put(`${API_URL}/auth/me`, userData, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const createChat = async (token, participants) => {
       try {
         const response = await axios.post(`${API_URL}/chats`, { participants }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChats = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/chats`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const logout = () => {
       localStorage.removeItem('token');
       window.location.href = '/login'; // Redirect to login page
     };
     ```

2. **Chat List Page**:
   - **Create Chat List Component** (`src/components/ChatList.js`):
     ```javascript
     import { useEffect, useState } from 'react';
     import { getChats } from '../services/api';

     export default function ChatList() {
       const [chats, setChats] = useState([]);
       const [error, setError] = useState('');

       useEffect(() => {
         const fetchChats = async () => {
           try {
             const token = localStorage.getItem('token');
             if (token) {
               const data = await getChats(token);
               setChats(data);
             } else {
               setError('No token found');
             }
           } catch (err) {
             setError(err.message);
           }
         };

         fetchChats();
       }, []);

       if (error) {
         return <div className="text-red-500">{error}</div>;
       }

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Chats</h2>
           {chats.length > 0 ? (
             <ul>
               {chats.map((chat) => (
                 <li key={chat._id} className="mb-2 p-2 border rounded">
                   {chat.participants.map((participant) => participant.username).join(', ')}
                 </li>
               ))}
             </ul>
           ) : (
             <p>No chats available.</p>
           )}
         </div>
       );
     }
     ```

### Conclusion

This setup includes the backend implementation for fetching all chat sessions for the logged-in user with NestJS and a simple frontend chat list component with Next.js. The chat fetching endpoint is protected with JWT authentication, ensuring only authenticated users can fetch their chat sessions.

### Chat Management: Fetch Chat Details

#### Backend Code (NestJS)

1. **Chat Service**:
   - **Update Chat Service to Handle Fetching Chat Details** (`src/chat/chat.service.ts`):
     ```typescript
     import { Injectable } from '@nestjs/common';
     import { InjectModel } from '@nestjs/mongoose';
     import { Model } from 'mongoose';
     import { Chat, ChatDocument } from '../schemas/chat.schema';
     import { User, UserDocument } from '../schemas/user.schema';

     @Injectable()
     export class ChatService {
       constructor(
         @InjectModel(Chat.name) private chatModel: Model<ChatDocument>,
         @InjectModel(User.name) private userModel: Model<UserDocument>,
       ) {}

       async createChat(participants: string[]): Promise<Chat> {
         const newChat = new this.chatModel({
           participants: participants,
           messages: [],
         });
         return newChat.save();
       }

       async getChats(userId: string): Promise<Chat[]> {
         return this.chatModel.find({ participants: userId }).populate('participants', 'username').exec();
       }

       async getChatDetails(chatId: string): Promise<Chat> {
         return this.chatModel.findById(chatId).populate('participants', 'username').populate('messages.sender', 'username').exec();
       }
     }
     ```

2. **Chat Controller**:
   - **Update Chat Controller to Include Fetching Chat Details** (`src/chat/chat.controller.ts`):
     ```typescript
     import { Controller, Post, Get, Param, Body, UseGuards, Request } from '@nestjs/common';
     import { ChatService } from './chat.service';
     import { CreateChatDto } from './dto/create-chat.dto';
     import { JwtAuthGuard } from '../auth/jwt-auth.guard';
     import { Chat } from '../schemas/chat.schema';

     @Controller('chats')
     export class ChatController {
       constructor(private readonly chatService: ChatService) {}

       @UseGuards(JwtAuthGuard)
       @Post()
       async createChat(@Body() createChatDto: CreateChatDto): Promise<Chat> {
         return this.chatService.createChat(createChatDto.participants);
       }

       @UseGuards(JwtAuthGuard)
       @Get()
       async getChats(@Request() req): Promise<Chat[]> {
         return this.chatService.getChats(req.user.userId);
       }

       @UseGuards(JwtAuthGuard)
       @Get(':chatId')
       async getChatDetails(@Param('chatId') chatId: string): Promise<Chat> {
         return this.chatService.getChatDetails(chatId);
       }
     }
     ```

3. **Ensure JWT Auth Guard is Applied**:
   - Ensure that `JwtAuthGuard` is properly set up and imported as shown in previous examples.

### Frontend Code (Next.js)

1. **API Call for Fetching Chat Details**:
   - **Create API Function for Fetching Chat Details** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const login = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/login`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getProfile = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/auth/me`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const updateProfile = async (token, userData) => {
       try {
         const response = await axios.put(`${API_URL}/auth/me`, userData, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const createChat = async (token, participants) => {
       try {
         const response = await axios.post(`${API_URL}/chats`, { participants }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChats = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/chats`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChatDetails = async (token, chatId) => {
       try {
         const response = await axios.get(`${API_URL}/chats/${chatId}`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const logout = () => {
       localStorage.removeItem('token');
       window.location.href = '/login'; // Redirect to login page
     };
     ```

2. **Chat Details Page**:
   - **Create Chat Details Component** (`src/components/ChatDetails.js`):
     ```javascript
     import { useEffect, useState } from 'react';
     import { getChatDetails } from '../services/api';

     export default function ChatDetails({ chatId }) {
       const [chat, setChat] = useState(null);
       const [error, setError] = useState('');

       useEffect(() => {
         const fetchChatDetails = async () => {
           try {
             const token = localStorage.getItem('token');
             if (token) {
               const data = await getChatDetails(token, chatId);
               setChat(data);
             } else {
               setError('No token found');
             }
           } catch (err) {
             setError(err.message);
           }
         };

         fetchChatDetails();
       }, [chatId]);

       if (error) {
         return <div className="text-red-500">{error}</div>;
       }

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Chat Details</h2>
           {chat ? (
             <div>
               <h3 className="text-xl font-bold mb-3">Participants</h3>
               <ul>
                 {chat.participants.map((participant) => (
                   <li key={participant._id}>{participant.username}</li>
                 ))}
               </ul>
               <h3 className="text-xl font-bold mb-3 mt-5">Messages</h3>
               <ul>
                 {chat.messages.map((message) => (
                   <li key={message._id} className="mb-2 p-2 border rounded">
                     <strong>{message.sender.username}</strong>: {message.content} <br />
                     <span className="text-gray-500 text-sm">{new Date(message.timestamp).toLocaleString()}</span>
                   </li>
                 ))}
               </ul>
             </div>
           ) : (
             <p>Loading...</p>
           )}
         </div>
       );
     }
     ```

3. **Usage in a Page**:
   - **Create a Page to Display Chat Details** (`src/pages/chat/[chatId].js`):
     ```javascript
     import { useRouter } from 'next/router';
     import ChatDetails from '../../components/ChatDetails';

     export default function ChatPage() {
       const router = useRouter();
       const { chatId } = router.query;

       if (!chatId) {
         return <div>Loading...</div>;
       }

       return <ChatDetails chatId={chatId} />;
     }
     ```

### Conclusion

This setup includes the backend implementation for fetching details of a specific chat session with NestJS and a simple frontend chat details component with Next.js. The chat details endpoint is protected with JWT authentication, ensuring only authenticated users can fetch the details of their chat sessions.

### Messaging: Send Message

#### Backend Code (NestJS)

1. **Message DTO**:
   - **Create DTO for Sending Message** (`src/chat/dto/send-message.dto.ts`):
     ```typescript
     export class SendMessageDto {
       content: string;
     }
     ```

2. **Chat Schema Update**:
   - **Update Chat Schema to Include Messages** (`src/schemas/chat.schema.ts`):
     ```typescript
     import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
     import { Document, Types } from 'mongoose';

     export type ChatDocument = Chat & Document;

     @Schema()
     export class Chat {
       @Prop({ type: [{ type: Types.ObjectId, ref: 'User' }], required: true })
       participants: Types.ObjectId[];

       @Prop({ type: [{ sender: { type: Types.ObjectId, ref: 'User' }, content: String, timestamp: Date }], default: [] })
       messages: { sender: Types.ObjectId; content: string; timestamp: Date }[];
     }

     export const ChatSchema = SchemaFactory.createForClass(Chat);
     ```

3. **Chat Service**:
   - **Update Chat Service to Handle Sending Messages** (`src/chat/chat.service.ts`):
     ```typescript
     import { Injectable, NotFoundException } from '@nestjs/common';
     import { InjectModel } from '@nestjs/mongoose';
     import { Model } from 'mongoose';
     import { Chat, ChatDocument } from '../schemas/chat.schema';
     import { User, UserDocument } from '../schemas/user.schema';
     import { SendMessageDto } from './dto/send-message.dto';

     @Injectable()
     export class ChatService {
       constructor(
         @InjectModel(Chat.name) private chatModel: Model<ChatDocument>,
         @InjectModel(User.name) private userModel: Model<UserDocument>,
       ) {}

       async createChat(participants: string[]): Promise<Chat> {
         const newChat = new this.chatModel({
           participants: participants,
           messages: [],
         });
         return newChat.save();
       }

       async getChats(userId: string): Promise<Chat[]> {
         return this.chatModel.find({ participants: userId }).populate('participants', 'username').exec();
       }

       async getChatDetails(chatId: string): Promise<Chat> {
         return this.chatModel.findById(chatId).populate('participants', 'username').populate('messages.sender', 'username').exec();
       }

       async sendMessage(chatId: string, userId: string, sendMessageDto: SendMessageDto): Promise<Chat> {
         const chat = await this.chatModel.findById(chatId);
         if (!chat) {
           throw new NotFoundException('Chat not found');
         }

         chat.messages.push({
           sender: userId,
           content: sendMessageDto.content,
           timestamp: new Date(),
         });

         return chat.save();
       }
     }
     ```

4. **Chat Controller**:
   - **Update Chat Controller to Include Sending Messages** (`src/chat/chat.controller.ts`):
     ```typescript
     import { Controller, Post, Get, Param, Body, UseGuards, Request } from '@nestjs/common';
     import { ChatService } from './chat.service';
     import { CreateChatDto } from './dto/create-chat.dto';
     import { SendMessageDto } from './dto/send-message.dto';
     import { JwtAuthGuard } from '../auth/jwt-auth.guard';
     import { Chat } from '../schemas/chat.schema';

     @Controller('chats')
     export class ChatController {
       constructor(private readonly chatService: ChatService) {}

       @UseGuards(JwtAuthGuard)
       @Post()
       async createChat(@Body() createChatDto: CreateChatDto): Promise<Chat> {
         return this.chatService.createChat(createChatDto.participants);
       }

       @UseGuards(JwtAuthGuard)
       @Get()
       async getChats(@Request() req): Promise<Chat[]> {
         return this.chatService.getChats(req.user.userId);
       }

       @UseGuards(JwtAuthGuard)
       @Get(':chatId')
       async getChatDetails(@Param('chatId') chatId: string): Promise<Chat> {
         return this.chatService.getChatDetails(chatId);
       }

       @UseGuards(JwtAuthGuard)
       @Post(':chatId/messages')
       async sendMessage(@Param('chatId') chatId: string, @Request() req, @Body() sendMessageDto: SendMessageDto): Promise<Chat> {
         return this.chatService.sendMessage(chatId, req.user.userId, sendMessageDto);
       }
     }
     ```

5. **Ensure JWT Auth Guard is Applied**:
   - Ensure that `JwtAuthGuard` is properly set up and imported as shown in previous examples.

### Frontend Code (Next.js)

1. **API Call for Sending Messages**:
   - **Create API Function for Sending Messages** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const login = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/login`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getProfile = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/auth/me`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const updateProfile = async (token, userData) => {
       try {
         const response = await axios.put(`${API_URL}/auth/me`, userData, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const createChat = async (token, participants) => {
       try {
         const response = await axios.post(`${API_URL}/chats`, { participants }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChats = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/chats`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChatDetails = async (token, chatId) => {
       try {
         const response = await axios.get(`${API_URL}/chats/${chatId}`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const sendMessage = async (token, chatId, content) => {
       try {
         const response = await axios.post(`${API_URL}/chats/${chatId}/messages`, { content }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const logout = () => {
       localStorage.removeItem('token');
       window.location.href = '/login'; // Redirect to login page
     };
     ```

2. **Chat Details Component**:
   - **Update Chat Details Component to Include Sending Messages** (`src/components/ChatDetails.js`):
     ```javascript
     import { useEffect, useState } from 'react';
     import { getChatDetails, sendMessage } from '../services/api';

     export default function ChatDetails({ chatId }) {
       const [chat, setChat] = useState(null);
       const [messageContent, setMessageContent] = useState('');
       const [error, setError] = useState('');

       useEffect(() => {
         const fetchChatDetails = async () => {
           try {
             const token = localStorage.getItem('token');
             if (token) {
               const data = await getChatDetails(token, chatId);
               setChat(data);
             } else {
               setError('No token found');
             }
           } catch (err) {
             setError(err.message);
           }
         };

         fetchChatDetails();
       }, [chatId]);

       const handleSendMessage = async (e) => {
         e.preventDefault();
         try {
           const token = localStorage.getItem('token');
           if (token) {
             const data = await sendMessage(token, chatId, messageContent);
             setChat(data);
             setMessageContent('');
           } else {
             setError('No token found');
           }
         } catch (err) {
           setError(err.message);
         }
       };

       if (error) {
         return <div className="text-red-500">{error}</div>;
       }

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Chat Details</h2>
           {chat ? (
             <div>
               <h3 className="text-xl

 font-bold mb-3">Participants</h3>
               <ul>
                 {chat.participants.map((participant) => (
                   <li key={participant._id}>{participant.username}</li>
                 ))}
               </ul>
               <h3 className="text-xl font-bold mb-3 mt-5">Messages</h3>
               <ul>
                 {chat.messages.map((message) => (
                   <li key={message._id} className="mb-2 p-2 border rounded">
                     <strong>{message.sender.username}</strong>: {message.content} <br />
                     <span className="text-gray-500 text-sm">{new Date(message.timestamp).toLocaleString()}</span>
                   </li>
                 ))}
               </ul>
               <form onSubmit={handleSendMessage} className="mt-4">
                 <input
                   type="text"
                   value={messageContent}
                   onChange={(e) => setMessageContent(e.target.value)}
                   className="w-full px-3 py-2 border rounded mb-2"
                   placeholder="Type your message..."
                 />
                 <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">Send</button>
               </form>
             </div>
           ) : (
             <p>Loading...</p>
           )}
         </div>
       );
     }
     ```

3. **Usage in a Page**:
   - **Create a Page to Display Chat Details** (`src/pages/chat/[chatId].js`):
     ```javascript
     import { useRouter } from 'next/router';
     import ChatDetails from '../../components/ChatDetails';

     export default function ChatPage() {
       const router = useRouter();
       const { chatId } = router.query;

       if (!chatId) {
         return <div>Loading...</div>;
       }

       return <ChatDetails chatId={chatId} />;
     }
     ```

### Conclusion

This setup includes the backend implementation for sending messages in a specific chat session with NestJS and a simple frontend chat details component with Next.js. The chat details component now includes functionality for sending messages. The chat details and message sending endpoints are protected with JWT authentication, ensuring only authenticated users can fetch and send messages in their chat sessions.

### Messaging: Real-Time Message Receiving Using WebSocket

#### Backend Code (NestJS)

1. **WebSocket Gateway Setup**:
   - **Install WebSocket Dependencies**:
     ```bash
     npm install @nestjs/websockets @nestjs/platform-socket.io
     ```

2. **WebSocket Gateway**:
   - **Create WebSocket Gateway** (`src/chat/chat.gateway.ts`):
     ```typescript
     import {
       SubscribeMessage,
       WebSocketGateway,
       WebSocketServer,
       OnGatewayInit,
       OnGatewayConnection,
       OnGatewayDisconnect,
     } from '@nestjs/websockets';
     import { Server, Socket } from 'socket.io';
     import { ChatService } from './chat.service';
     import { SendMessageDto } from './dto/send-message.dto';

     @WebSocketGateway()
     export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
       @WebSocketServer() server: Server;

       constructor(private readonly chatService: ChatService) {}

       afterInit(server: Server) {
         console.log('Init');
       }

       handleConnection(client: Socket, ...args: any[]) {
         console.log(`Client connected: ${client.id}`);
       }

       handleDisconnect(client: Socket) {
         console.log(`Client disconnected: ${client.id}`);
       }

       @SubscribeMessage('sendMessage')
       async handleMessage(client: Socket, payload: { chatId: string; userId: string; content: string }) {
         const message: SendMessageDto = { content: payload.content };
         const chat = await this.chatService.sendMessage(payload.chatId, payload.userId, message);
         this.server.to(payload.chatId).emit('receiveMessage', chat);
       }

       @SubscribeMessage('joinChat')
       handleJoinChat(client: Socket, chatId: string) {
         client.join(chatId);
         console.log(`Client ${client.id} joined chat ${chatId}`);
       }
     }
     ```

3. **Chat Service** (Update for WebSocket):
   - **Update Chat Service to Handle WebSocket Messages** (`src/chat/chat.service.ts`):
     ```typescript
     import { Injectable, NotFoundException } from '@nestjs/common';
     import { InjectModel } from '@nestjs/mongoose';
     import { Model } from 'mongoose';
     import { Chat, ChatDocument } from '../schemas/chat.schema';
     import { User, UserDocument } from '../schemas/user.schema';
     import { SendMessageDto } from './dto/send-message.dto';

     @Injectable()
     export class ChatService {
       constructor(
         @InjectModel(Chat.name) private chatModel: Model<ChatDocument>,
         @InjectModel(User.name) private userModel: Model<UserDocument>,
       ) {}

       async createChat(participants: string[]): Promise<Chat> {
         const newChat = new this.chatModel({
           participants: participants,
           messages: [],
         });
         return newChat.save();
       }

       async getChats(userId: string): Promise<Chat[]> {
         return this.chatModel.find({ participants: userId }).populate('participants', 'username').exec();
       }

       async getChatDetails(chatId: string): Promise<Chat> {
         return this.chatModel.findById(chatId).populate('participants', 'username').populate('messages.sender', 'username').exec();
       }

       async sendMessage(chatId: string, userId: string, sendMessageDto: SendMessageDto): Promise<Chat> {
         const chat = await this.chatModel.findById(chatId);
         if (!chat) {
           throw new NotFoundException('Chat not found');
         }

         chat.messages.push({
           sender: userId,
           content: sendMessageDto.content,
           timestamp: new Date(),
         });

         return chat.save();
       }
     }
     ```

4. **Chat Module**:
   - **Update Chat Module to Include WebSocket Gateway** (`src/chat/chat.module.ts`):
     ```typescript
     import { Module } from '@nestjs/common';
     import { MongooseModule } from '@nestjs/mongoose';
     import { ChatService } from './chat.service';
     import { ChatController } from './chat.controller';
     import { ChatGateway } from './chat.gateway';
     import { Chat, ChatSchema } from '../schemas/chat.schema';
     import { User, UserSchema } from '../schemas/user.schema';

     @Module({
       imports: [
         MongooseModule.forFeature([{ name: Chat.name, schema: ChatSchema }]),
         MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
       ],
       providers: [ChatService, ChatGateway],
       controllers: [ChatController],
     })
     export class ChatModule {}
     ```

### Frontend Code (Next.js)

1. **WebSocket Client Setup**:
   - **Install Socket.io Client**:
     ```bash
     npm install socket.io-client
     ```

2. **WebSocket Client**:
   - **Create WebSocket Client Hook** (`src/hooks/useChat.js`):
     ```javascript
     import { useEffect, useState } from 'react';
     import io from 'socket.io-client';

     const useChat = (chatId) => {
       const [messages, setMessages] = useState([]);
       const [message, setMessage] = useState('');
       const [socket, setSocket] = useState(null);

       useEffect(() => {
         const newSocket = io('http://localhost:3000');
         setSocket(newSocket);

         newSocket.emit('joinChat', chatId);

         newSocket.on('receiveMessage', (newChat) => {
           setMessages(newChat.messages);
         });

         return () => newSocket.close();
       }, [chatId]);

       const sendMessage = (userId, content) => {
         socket.emit('sendMessage', { chatId, userId, content });
         setMessage('');
       };

       return {
         messages,
         message,
         setMessage,
         sendMessage,
       };
     };

     export default useChat;
     ```

3. **Chat Details Component**:
   - **Update Chat Details Component to Include WebSocket** (`src/components/ChatDetails.js`):
     ```javascript
     import { useRouter } from 'next/router';
     import useChat from '../hooks/useChat';

     export default function ChatDetails({ chatId, userId }) {
       const router = useRouter();
       const { messages, message, setMessage, sendMessage } = useChat(chatId);

       const handleSendMessage = (e) => {
         e.preventDefault();
         sendMessage(userId, message);
       };

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Chat Details</h2>
           <div>
             <h3 className="text-xl font-bold mb-3">Messages</h3>
             <ul>
               {messages.map((msg, index) => (
                 <li key={index} className="mb-2 p-2 border rounded">
                   <strong>{msg.sender.username}</strong>: {msg.content} <br />
                   <span className="text-gray-500 text-sm">{new Date(msg.timestamp).toLocaleString()}</span>
                 </li>
               ))}
             </ul>
             <form onSubmit={handleSendMessage} className="mt-4">
               <input
                 type="text"
                 value={message}
                 onChange={(e) => setMessage(e.target.value)}
                 className="w-full px-3 py-2 border rounded mb-2"
                 placeholder="Type your message..."
               />
               <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">Send</button>
             </form>
           </div>
         </div>
       );
     }
     ```

4. **Usage in a Page**:
   - **Update Page to Include User ID** (`src/pages/chat/[chatId].js`):
     ```javascript
     import { useRouter } from 'next/router';
     import ChatDetails from '../../components/ChatDetails';

     export default function ChatPage() {
       const router = useRouter();
       const { chatId } = router.query;
       const userId = 'USER_ID'; // Replace with the actual user ID from authentication

       if (!chatId) {
         return <div>Loading...</div>;
       }

       return <ChatDetails chatId={chatId} userId={userId} />;
     }
     ```

### Conclusion

This setup includes the backend implementation for real-time message receiving using WebSocket with NestJS and a simple frontend implementation with Next.js using Socket.io. The `ChatGateway` handles the WebSocket events, and the frontend uses a custom hook to manage WebSocket connections and message state. This allows for real-time messaging capabilities in your chat application.

### Real-time Communication: WebSocket Setup

#### Backend Code (NestJS)

1. **WebSocket Gateway Setup**:
   - **Install WebSocket Dependencies**:
     ```bash
     npm install @nestjs/websockets @nestjs/platform-socket.io
     ```

2. **WebSocket Gateway**:
   - **Create WebSocket Gateway** (`src/chat/chat.gateway.ts`):
     ```typescript
     import {
       SubscribeMessage,
       WebSocketGateway,
       WebSocketServer,
       OnGatewayInit,
       OnGatewayConnection,
       OnGatewayDisconnect,
     } from '@nestjs/websockets';
     import { Server, Socket } from 'socket.io';
     import { ChatService } from './chat.service';
     import { SendMessageDto } from './dto/send-message.dto';

     @WebSocketGateway()
     export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
       @WebSocketServer() server: Server;

       constructor(private readonly chatService: ChatService) {}

       afterInit(server: Server) {
         console.log('WebSocket server initialized');
       }

       handleConnection(client: Socket) {
         console.log(`Client connected: ${client.id}`);
       }

       handleDisconnect(client: Socket) {
         console.log(`Client disconnected: ${client.id}`);
       }

       @SubscribeMessage('sendMessage')
       async handleMessage(client: Socket, payload: { chatId: string; userId: string; content: string }) {
         const message: SendMessageDto = { content: payload.content };
         const chat = await this.chatService.sendMessage(payload.chatId, payload.userId, message);
         this.server.to(payload.chatId).emit('receiveMessage', chat);
       }

       @SubscribeMessage('joinChat')
       handleJoinChat(client: Socket, chatId: string) {
         client.join(chatId);
         console.log(`Client ${client.id} joined chat ${chatId}`);
       }
     }
     ```

3. **Chat Service Update**:
   - **Update Chat Service to Handle WebSocket Messages** (`src/chat/chat.service.ts`):
     ```typescript
     import { Injectable, NotFoundException } from '@nestjs/common';
     import { InjectModel } from '@nestjs/mongoose';
     import { Model } from 'mongoose';
     import { Chat, ChatDocument } from '../schemas/chat.schema';
     import { User, UserDocument } from '../schemas/user.schema';
     import { SendMessageDto } from './dto/send-message.dto';

     @Injectable()
     export class ChatService {
       constructor(
         @InjectModel(Chat.name) private chatModel: Model<ChatDocument>,
         @InjectModel(User.name) private userModel: Model<UserDocument>,
       ) {}

       async createChat(participants: string[]): Promise<Chat> {
         const newChat = new this.chatModel({
           participants: participants,
           messages: [],
         });
         return newChat.save();
       }

       async getChats(userId: string): Promise<Chat[]> {
         return this.chatModel.find({ participants: userId }).populate('participants', 'username').exec();
       }

       async getChatDetails(chatId: string): Promise<Chat> {
         return this.chatModel.findById(chatId).populate('participants', 'username').populate('messages.sender', 'username').exec();
       }

       async sendMessage(chatId: string, userId: string, sendMessageDto: SendMessageDto): Promise<Chat> {
         const chat = await this.chatModel.findById(chatId);
         if (!chat) {
           throw new NotFoundException('Chat not found');
         }

         chat.messages.push({
           sender: userId,
           content: sendMessageDto.content,
           timestamp: new Date(),
         });

         return chat.save();
       }
     }
     ```

4. **Chat Module Update**:
   - **Update Chat Module to Include WebSocket Gateway** (`src/chat/chat.module.ts`):
     ```typescript
     import { Module } from '@nestjs/common';
     import { MongooseModule } from '@nestjs/mongoose';
     import { ChatService } from './chat.service';
     import { ChatController } from './chat.controller';
     import { ChatGateway } from './chat.gateway';
     import { Chat, ChatSchema } from '../schemas/chat.schema';
     import { User, UserSchema } from '../schemas/user.schema';

     @Module({
       imports: [
         MongooseModule.forFeature([{ name: Chat.name, schema: ChatSchema }]),
         MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
       ],
       providers: [ChatService, ChatGateway],
       controllers: [ChatController],
     })
     export class ChatModule {}
     ```

### Frontend Code (Next.js)

1. **WebSocket Client Setup**:
   - **Install Socket.io Client**:
     ```bash
     npm install socket.io-client
     ```

2. **WebSocket Client Hook**:
   - **Create WebSocket Client Hook** (`src/hooks/useChat.js`):
     ```javascript
     import { useEffect, useState } from 'react';
     import io from 'socket.io-client';

     const useChat = (chatId) => {
       const [messages, setMessages] = useState([]);
       const [message, setMessage] = useState('');
       const [socket, setSocket] = useState(null);

       useEffect(() => {
         const newSocket = io('http://localhost:3000');
         setSocket(newSocket);

         newSocket.emit('joinChat', chatId);

         newSocket.on('receiveMessage', (newChat) => {
           setMessages(newChat.messages);
         });

         return () => newSocket.close();
       }, [chatId]);

       const sendMessage = (userId, content) => {
         socket.emit('sendMessage', { chatId, userId, content });
         setMessage('');
       };

       return {
         messages,
         message,
         setMessage,
         sendMessage,
       };
     };

     export default useChat;
     ```

3. **Chat Details Component**:
   - **Update Chat Details Component to Include WebSocket** (`src/components/ChatDetails.js`):
     ```javascript
     import { useEffect, useState } from 'react';
     import useChat from '../hooks/useChat';

     export default function ChatDetails({ chatId, userId }) {
       const { messages, message, setMessage, sendMessage } = useChat(chatId);

       const handleSendMessage = (e) => {
         e.preventDefault();
         sendMessage(userId, message);
       };

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Chat Details</h2>
           <div>
             <h3 className="text-xl font-bold mb-3">Messages</h3>
             <ul>
               {messages.map((msg, index) => (
                 <li key={index} className="mb-2 p-2 border rounded">
                   <strong>{msg.sender.username}</strong>: {msg.content} <br />
                   <span className="text-gray-500 text-sm">{new Date(msg.timestamp).toLocaleString()}</span>
                 </li>
               ))}
             </ul>
             <form onSubmit={handleSendMessage} className="mt-4">
               <input
                 type="text"
                 value={message}
                 onChange={(e) => setMessage(e.target.value)}
                 className="w-full px-3 py-2 border rounded mb-2"
                 placeholder="Type your message..."
               />
               <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">Send</button>
             </form>
           </div>
         </div>
       );
     }
     ```

4. **Usage in a Page**:
   - **Update Page to Include User ID** (`src/pages/chat/[chatId].js`):
     ```javascript
     import { useRouter } from 'next/router';
     import ChatDetails from '../../components/ChatDetails';

     export default function ChatPage() {
       const router = useRouter();
       const { chatId } = router.query;
       const userId = 'USER_ID'; // Replace with the actual user ID from authentication

       if (!chatId) {
         return <div>Loading...</div>;
       }

       return <ChatDetails chatId={chatId} userId={userId} />;
     }
     ```

### Conclusion

This setup includes the backend implementation for real-time message receiving and sending using WebSocket with NestJS and a simple frontend implementation with Next.js using Socket.io. The `ChatGateway` handles the WebSocket events, and the frontend uses a custom hook to manage WebSocket connections and message state. This allows for real-time messaging capabilities in your chat application, ensuring that messages are sent and received in real time.

### User Interface: Login Page

#### Frontend Code (Next.js)

1. **Create Login Page**:
   - **Create Login Page Component** (`src/pages/login.js`):
     ```javascript
     import { useState } from 'react';
     import { useRouter } from 'next/router';
     import { login } from '../services/api';

     export default function LoginPage() {
       const [username, setUsername] = useState('');
       const [password, setPassword] = useState('');
       const [error, setError] = useState('');
       const router = useRouter();

       const handleSubmit = async (e) => {
         e.preventDefault();
         try {
           const data = await login(username, password);
           localStorage.setItem('token', data.access_token);
           router.push('/chats');
         } catch (err) {
           setError(err.message);
         }
       };

       return (
         <div className="flex items-center justify-center h-screen bg-gray-100">
           <div className="w-full max-w-md p-8 space-y-8 bg-white rounded shadow-lg">
             <h2 className="text-2xl font-bold text-center">Login</h2>
             <form className="space-y-6" onSubmit={handleSubmit}>
               <div>
                 <label className="block text-gray-700">Username</label>
                 <input
                   type="text"
                   value={username}
                   onChange={(e) => setUsername(e.target.value)}
                   className="w-full px-3 py-2 border rounded"
                   required
                 />
               </div>
               <div>
                 <label className="block text-gray-700">Password</label>
                 <input
                   type="password"
                   value={password}
                   onChange={(e) => setPassword(e.target.value)}
                   className="w-full px-3 py-2 border rounded"
                   required
                 />
               </div>
               <div>
                 <button
                   type="submit"
                   className="w-full px-4 py-2 text-white bg-blue-500 rounded hover:bg-blue-700"
                 >
                   Login
                 </button>
               </div>
               {error && <p className="text-red-500">{error}</p>}
             </form>
           </div>
         </div>
       );
     }
     ```

2. **API Call for Login**:
   - **Update API Function for Login** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const login = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/login`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getProfile = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/auth/me`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const updateProfile = async (token, userData) => {
       try {
         const response = await axios.put(`${API_URL}/auth/me`, userData, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const createChat = async (token, participants) => {
       try {
         const response = await axios.post(`${API_URL}/chats`, { participants }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChats = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/chats`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChatDetails = async (token, chatId) => {
       try {
         const response = await axios.get(`${API_URL}/chats/${chatId}`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const sendMessage = async (token, chatId, content) => {
       try {
         const response = await axios.post(`${API_URL}/chats/${chatId}/messages`, { content }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const logout = () => {
       localStorage.removeItem('token');
       window.location.href = '/login'; // Redirect to login page
     };
     ```

### Conclusion

This setup includes the frontend implementation of a login page using Next.js and Tailwind CSS. The login page consists of a form with input fields for the username and password, and a submit button to log in. The login API call is handled using an async function that communicates with the backend. Upon successful login, the user is redirected to the chat page. Any error during login is displayed to the user.

### User Interface: Registration Page

#### Frontend Code (Next.js)

1. **Create Registration Page**:
   - **Create Registration Page Component** (`src/pages/register.js`):
     ```javascript
     import { useState } from 'react';
     import { useRouter } from 'next/router';
     import { register } from '../services/api';

     export default function RegisterPage() {
       const [username, setUsername] = useState('');
       const [password, setPassword] = useState('');
       const [error, setError] = useState('');
       const [success, setSuccess] = useState('');
       const router = useRouter();

       const handleSubmit = async (e) => {
         e.preventDefault();
         try {
           await register(username, password);
           setSuccess('Registration successful! You can now login.');
           setUsername('');
           setPassword('');
           setError('');
           setTimeout(() => {
             router.push('/login');
           }, 2000);
         } catch (err) {
           setError(err.message);
           setSuccess('');
         }
       };

       return (
         <div className="flex items-center justify-center h-screen bg-gray-100">
           <div className="w-full max-w-md p-8 space-y-8 bg-white rounded shadow-lg">
             <h2 className="text-2xl font-bold text-center">Register</h2>
             <form className="space-y-6" onSubmit={handleSubmit}>
               <div>
                 <label className="block text-gray-700">Username</label>
                 <input
                   type="text"
                   value={username}
                   onChange={(e) => setUsername(e.target.value)}
                   className="w-full px-3 py-2 border rounded"
                   required
                 />
               </div>
               <div>
                 <label className="block text-gray-700">Password</label>
                 <input
                   type="password"
                   value={password}
                   onChange={(e) => setPassword(e.target.value)}
                   className="w-full px-3 py-2 border rounded"
                   required
                 />
               </div>
               <div>
                 <button
                   type="submit"
                   className="w-full px-4 py-2 text-white bg-blue-500 rounded hover:bg-blue-700"
                 >
                   Register
                 </button>
               </div>
               {error && <p className="text-red-500">{error}</p>}
               {success && <p className="text-green-500">{success}</p>}
             </form>
           </div>
         </div>
       );
     }
     ```

2. **API Call for Registration**:
   - **Update API Function for Registration** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const login = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/login`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getProfile = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/auth/me`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const updateProfile = async (token, userData) => {
       try {
         const response = await axios.put(`${API_URL}/auth/me`, userData, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const createChat = async (token, participants) => {
       try {
         const response = await axios.post(`${API_URL}/chats`, { participants }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChats = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/chats`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChatDetails = async (token, chatId) => {
       try {
         const response = await axios.get(`${API_URL}/chats/${chatId}`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const sendMessage = async (token, chatId, content) => {
       try {
         const response = await axios.post(`${API_URL}/chats/${chatId}/messages`, { content }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const logout = () => {
       localStorage.removeItem('token');
       window.location.href = '/login'; // Redirect to login page
     };
     ```

### Conclusion

This setup includes the frontend implementation of a registration page using Next.js and Tailwind CSS. The registration page consists of a form with input fields for the username and password, and a submit button to register. The registration API call is handled using an async function that communicates with the backend. Upon successful registration, the user is notified and redirected to the login page. Any error during registration is displayed to the user.

### User Interface: Chat List Page and Chat Window

#### Frontend Code (Next.js)

### Chat List Page

1. **Create Chat List Page**:
   - **Create Chat List Page Component** (`src/pages/chats.js`):
     ```javascript
     import { useEffect, useState } from 'react';
     import { useRouter } from 'next/router';
     import { getChats } from '../services/api';

     export default function ChatListPage() {
       const [chats, setChats] = useState([]);
       const [search, setSearch] = useState('');
       const [error, setError] = useState('');
       const router = useRouter();

       useEffect(() => {
         const fetchChats = async () => {
           try {
             const token = localStorage.getItem('token');
             if (token) {
               const data = await getChats(token);
               setChats(data);
             } else {
               setError('No token found');
             }
           } catch (err) {
             setError(err.message);
           }
         };

         fetchChats();
       }, []);

       const filteredChats = chats.filter(chat =>
         chat.participants.some(participant =>
           participant.username.toLowerCase().includes(search.toLowerCase())
         )
       );

       const handleChatClick = (chatId) => {
         router.push(`/chat/${chatId}`);
       };

       if (error) {
         return <div className="text-red-500">{error}</div>;
       }

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Chats</h2>
           <input
             type="text"
             value={search}
             onChange={(e) => setSearch(e.target.value)}
             className="w-full px-3 py-2 border rounded mb-5"
             placeholder="Search chats..."
           />
           {filteredChats.length > 0 ? (
             <ul>
               {filteredChats.map(chat => (
                 <li
                   key={chat._id}
                   className="mb-2 p-2 border rounded cursor-pointer"
                   onClick={() => handleChatClick(chat._id)}
                 >
                   {chat.participants.map(participant => participant.username).join(', ')}
                 </li>
               ))}
             </ul>
           ) : (
             <p>No chats available.</p>
           )}
         </div>
       );
     }
     ```

### Chat Window

1. **Create Chat Window Component**:
   - **Create Chat Window Component** (`src/components/ChatWindow.js`):
     ```javascript
     import { useEffect, useState } from 'react';
     import useChat from '../hooks/useChat';

     export default function ChatWindow({ chatId, userId }) {
       const { messages, message, setMessage, sendMessage } = useChat(chatId);

       const handleSendMessage = (e) => {
         e.preventDefault();
         sendMessage(userId, message);
       };

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Chat</h2>
           <div>
             <ul className="mb-5">
               {messages.map((msg, index) => (
                 <li key={index} className="mb-2 p-2 border rounded">
                   <strong>{msg.sender.username}</strong>: {msg.content} <br />
                   <span className="text-gray-500 text-sm">{new Date(msg.timestamp).toLocaleString()}</span>
                 </li>
               ))}
             </ul>
             <form onSubmit={handleSendMessage}>
               <input
                 type="text"
                 value={message}
                 onChange={(e) => setMessage(e.target.value)}
                 className="w-full px-3 py-2 border rounded mb-2"
                 placeholder="Type your message..."
               />
               <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
                 Send
               </button>
             </form>
           </div>
         </div>
       );
     }
     ```

2. **WebSocket Client Hook** (from previous steps):
   - **Ensure you have the `useChat` hook** (`src/hooks/useChat.js`):
     ```javascript
     import { useEffect, useState } from 'react';
     import io from 'socket.io-client';

     const useChat = (chatId) => {
       const [messages, setMessages] = useState([]);
       const [message, setMessage] = useState('');
       const [socket, setSocket] = useState(null);

       useEffect(() => {
         const newSocket = io('http://localhost:3000');
         setSocket(newSocket);

         newSocket.emit('joinChat', chatId);

         newSocket.on('receiveMessage', (newChat) => {
           setMessages(newChat.messages);
         });

         return () => newSocket.close();
       }, [chatId]);

       const sendMessage = (userId, content) => {
         socket.emit('sendMessage', { chatId, userId, content });
         setMessage('');
       };

       return {
         messages,
         message,
         setMessage,
         sendMessage,
       };
     };

     export default useChat;
     ```

3. **Usage in a Page**:
   - **Create Page for Chat Window** (`src/pages/chat/[chatId].js`):
     ```javascript
     import { useRouter } from 'next/router';
     import ChatWindow from '../../components/ChatWindow';

     export default function ChatPage() {
       const router = useRouter();
       const { chatId } = router.query;
       const userId = 'USER_ID'; // Replace with the actual user ID from authentication

       if (!chatId) {
         return <div>Loading...</div>;
       }

       return <ChatWindow chatId={chatId} userId={userId} />;
     }
     ```

### Conclusion

This setup includes the frontend implementation for the chat list page and chat window using Next.js and Tailwind CSS. The chat list page displays the list of chat sessions with a search bar, allowing users to filter chats. The chat window displays chat messages and includes an input field and send button for sending new messages. The `useChat` hook manages the WebSocket connection and real-time message handling.

### Notifications: Real-time Notifications for New Messages

#### Frontend Code (Next.js)

1. **WebSocket Client Hook Enhancement**:
   - **Update `useChat` Hook to Handle Notifications** (`src/hooks/useChat.js`):
     ```javascript
     import { useEffect, useState } from 'react';
     import io from 'socket.io-client';

     const useChat = (chatId, onNewMessage) => {
       const [messages, setMessages] = useState([]);
       const [message, setMessage] = useState('');
       const [socket, setSocket] = useState(null);

       useEffect(() => {
         const newSocket = io('http://localhost:3000');
         setSocket(newSocket);

         newSocket.emit('joinChat', chatId);

         newSocket.on('receiveMessage', (newChat) => {
           setMessages(newChat.messages);
           if (onNewMessage) {
             const newMessage = newChat.messages[newChat.messages.length - 1];
             onNewMessage(newMessage);
           }
         });

         return () => newSocket.close();
       }, [chatId]);

       const sendMessage = (userId, content) => {
         socket.emit('sendMessage', { chatId, userId, content });
         setMessage('');
       };

       return {
         messages,
         message,
         setMessage,
         sendMessage,
       };
     };

     export default useChat;
     ```

2. **Notification Component**:
   - **Create Notification Component** (`src/components/Notification.js`):
     ```javascript
     export default function Notification({ message }) {
       if (!message) return null;

       return (
         <div className="fixed bottom-0 right-0 mb-4 mr-4 p-4 bg-blue-500 text-white rounded shadow-lg">
           <strong>{message.sender.username}</strong>: {message.content}
         </div>
       );
     }
     ```

3. **Chat Details Component with Notifications**:
   - **Update Chat Details Component to Show Notifications** (`src/components/ChatDetails.js`):
     ```javascript
     import { useState } from 'react';
     import useChat from '../hooks/useChat';
     import Notification from './Notification';

     export default function ChatDetails({ chatId, userId }) {
       const [notification, setNotification] = useState(null);
       const { messages, message, setMessage, sendMessage } = useChat(chatId, setNotification);

       const handleSendMessage = (e) => {
         e.preventDefault();
         sendMessage(userId, message);
       };

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Chat</h2>
           <div>
             <ul className="mb-5">
               {messages.map((msg, index) => (
                 <li key={index} className="mb-2 p-2 border rounded">
                   <strong>{msg.sender.username}</strong>: {msg.content} <br />
                   <span className="text-gray-500 text-sm">{new Date(msg.timestamp).toLocaleString()}</span>
                 </li>
               ))}
             </ul>
             <form onSubmit={handleSendMessage}>
               <input
                 type="text"
                 value={message}
                 onChange={(e) => setMessage(e.target.value)}
                 className="w-full px-3 py-2 border rounded mb-2"
                 placeholder="Type your message..."
               />
               <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
                 Send
               </button>
             </form>
           </div>
           <Notification message={notification} />
         </div>
       );
     }
     ```

4. **Usage in a Page**:
   - **Ensure Page Includes User ID** (`src/pages/chat/[chatId].js`):
     ```javascript
     import { useRouter } from 'next/router';
     import ChatDetails from '../../components/ChatDetails';

     export default function ChatPage() {
       const router = useRouter();
       const { chatId } = router.query;
       const userId = 'USER_ID'; // Replace with the actual user ID from authentication

       if (!chatId) {
         return <div>Loading...</div>;
       }

       return <ChatDetails chatId={chatId} userId={userId} />;
     }
     ```

### Conclusion

This setup includes the frontend implementation for real-time notifications for new messages using Next.js. The `useChat` hook is enhanced to notify the component of new messages. A `Notification` component displays the notification at the bottom right corner of the screen. The `ChatDetails` component is updated to show notifications whenever a new message is received. This ensures that users are promptly notified of new messages in real-time.

### File Sharing: Upload and Download Files

#### Backend Code (NestJS)

1. **Install Required Dependencies**:
   - **Install Multer for File Uploads**:
     ```bash
     npm install @nestjs/platform-express multer
     ```

2. **File Schema**:
   - **Create File Schema** (`src/schemas/file.schema.ts`):
     ```typescript
     import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
     import { Document, Types } from 'mongoose';

     export type FileDocument = File & Document;

     @Schema()
     export class File {
       @Prop({ required: true })
       filename: string;

       @Prop({ required: true })
       path: string;

       @Prop({ required: true })
       mimetype: string;

       @Prop({ type: Types.ObjectId, ref: 'Chat' })
       chat: Types.ObjectId;
     }

     export const FileSchema = SchemaFactory.createForClass(File);
     ```

3. **Chat Schema Update**:
   - **Update Chat Schema to Include Files** (`src/schemas/chat.schema.ts`):
     ```typescript
     import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
     import { Document, Types } from 'mongoose';

     export type ChatDocument = Chat & Document;

     @Schema()
     export class Chat {
       @Prop({ type: [{ type: Types.ObjectId, ref: 'User' }], required: true })
       participants: Types.ObjectId[];

       @Prop({ type: [{ sender: { type: Types.ObjectId, ref: 'User' }, content: String, timestamp: Date }], default: [] })
       messages: { sender: Types.ObjectId; content: string; timestamp: Date }[];

       @Prop({ type: [{ type: Types.ObjectId, ref: 'File' }], default: [] })
       files: Types.ObjectId[];
     }

     export const ChatSchema = SchemaFactory.createForClass(Chat);
     ```

4. **File Service**:
   - **Create File Service to Handle File Upload and Download** (`src/file/file.service.ts`):
     ```typescript
     import { Injectable, NotFoundException } from '@nestjs/common';
     import { InjectModel } from '@nestjs/mongoose';
     import { Model } from 'mongoose';
     import { Chat, ChatDocument } from '../schemas/chat.schema';
     import { File, FileDocument } from '../schemas/file.schema';
     import { Express } from 'express';
     import { join } from 'path';

     @Injectable()
     export class FileService {
       constructor(
         @InjectModel(File.name) private fileModel: Model<FileDocument>,
         @InjectModel(Chat.name) private chatModel: Model<ChatDocument>,
       ) {}

       async uploadFile(chatId: string, file: Express.Multer.File): Promise<File> {
         const chat = await this.chatModel.findById(chatId);
         if (!chat) {
           throw new NotFoundException('Chat not found');
         }

         const newFile = new this.fileModel({
           filename: file.filename,
           path: file.path,
           mimetype: file.mimetype,
           chat: chatId,
         });

         chat.files.push(newFile._id);
         await chat.save();
         return newFile.save();
       }

       async downloadFile(fileId: string): Promise<File> {
         const file = await this.fileModel.findById(fileId);
         if (!file) {
           throw new NotFoundException('File not found');
         }
         return file;
       }

       getFilePath(file: File): string {
         return join(__dirname, '..', '..', file.path);
       }
     }
     ```

5. **File Controller**:
   - **Create File Controller to Handle File Upload and Download** (`src/file/file.controller.ts`):
     ```typescript
     import { Controller, Post, Get, Param, UploadedFile, UseInterceptors, Res, NotFoundException } from '@nestjs/common';
     import { FileInterceptor } from '@nestjs/platform-express';
     import { FileService } from './file.service';
     import { diskStorage } from 'multer';
     import { v4 as uuidv4 } from 'uuid';
     import { Express, Response } from 'express';

     @Controller('chats/:chatId/files')
     export class FileController {
       constructor(private readonly fileService: FileService) {}

       @Post()
       @UseInterceptors(
         FileInterceptor('file', {
           storage: diskStorage({
             destination: './uploads',
             filename: (req, file, cb) => {
               const filename = `${uuidv4()}-${file.originalname}`;
               cb(null, filename);
             },
           }),
         }),
       )
       async uploadFile(
         @Param('chatId') chatId: string,
         @UploadedFile() file: Express.Multer.File,
       ) {
         return this.fileService.uploadFile(chatId, file);
       }

       @Get(':fileId')
       async downloadFile(
         @Param('fileId') fileId: string,
         @Res() res: Response,
       ) {
         const file = await this.fileService.downloadFile(fileId);
         if (!file) {
           throw new NotFoundException('File not found');
         }
         res.sendFile(this.fileService.getFilePath(file));
       }
     }
     ```

6. **File Module**:
   - **Create File Module** (`src/file/file.module.ts`):
     ```typescript
     import { Module } from '@nestjs/common';
     import { MongooseModule } from '@nestjs/mongoose';
     import { FileService } from './file.service';
     import { FileController } from './file.controller';
     import { File, FileSchema } from '../schemas/file.schema';
     import { Chat, ChatSchema } from '../schemas/chat.schema';

     @Module({
       imports: [
         MongooseModule.forFeature([{ name: File.name, schema: FileSchema }]),
         MongooseModule.forFeature([{ name: Chat.name, schema: ChatSchema }]),
       ],
       providers: [FileService],
       controllers: [FileController],
     })
     export class FileModule {}
     ```

7. **App Module Update**:
   - **Update App Module to Include File Module** (`src/app.module.ts`):
     ```typescript
     import { Module } from '@nestjs/common';
     import { MongooseModule } from '@nestjs/mongoose';
     import { AuthModule } from './auth/auth.module';
     import { ChatModule } from './chat/chat.module';
     import { FileModule } from './file/file.module';

     @Module({
       imports: [
         MongooseModule.forRoot('mongodb://localhost/chat-app'),
         AuthModule,
         ChatModule,
         FileModule,
       ],
     })
     export class AppModule {}
     ```

#### Frontend Code (Next.js)

1. **API Call for File Upload and Download**:
   - **Create API Functions for File Upload and Download** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const login = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/login`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getProfile = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/auth/me`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const updateProfile = async (token, userData) => {
       try {
         const response = await axios.put(`${API_URL}/auth/me`, userData, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const createChat = async (token, participants) => {
       try {
         const response = await axios.post(`${API_URL}/chats`, { participants }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChats = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/chats`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChatDetails = async (token, chatId) => {
       try {
         const response = await axios.get(`${API_URL}/chats/${chatId}`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const sendMessage = async (token, chatId, content) => {
       try {
         const response = await axios.post(`${API_URL}/chats/${chatId}/messages`, { content }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export

 const uploadFile = async (token, chatId, file) => {
       try {
         const formData = new FormData();
         formData.append('file', file);

         const response = await axios.post(`${API_URL}/chats/${chatId}/files`, formData, {
           headers: {
             Authorization: `Bearer ${token}`,
             'Content-Type': 'multipart/form-data',
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const downloadFile = async (token, chatId, fileId) => {
       try {
         const response = await axios.get(`${API_URL}/chats/${chatId}/files/${fileId}`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
           responseType: 'blob',
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const logout = () => {
       localStorage.removeItem('token');
       window.location.href = '/login'; // Redirect to login page
     };
     ```

2. **File Upload Component**:
   - **Create File Upload Component** (`src/components/FileUpload.js`):
     ```javascript
     import { useState } from 'react';
     import { uploadFile } from '../services/api';

     export default function FileUpload({ chatId }) {
       const [file, setFile] = useState(null);
       const [message, setMessage] = useState('');

       const handleFileChange = (e) => {
         setFile(e.target.files[0]);
       };

       const handleUpload = async (e) => {
         e.preventDefault();
         try {
           const token = localStorage.getItem('token');
           await uploadFile(token, chatId, file);
           setMessage('File uploaded successfully');
           setFile(null);
         } catch (err) {
           setMessage(`Error: ${err.message}`);
         }
       };

       return (
         <div className="mb-4">
           <form onSubmit={handleUpload}>
             <input type="file" onChange={handleFileChange} className="mb-2" />
             <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
               Upload File
             </button>
           </form>
           {message && <p className="mt-2">{message}</p>}
         </div>
       );
     }
     ```

3. **File Download Component**:
   - **Create File Download Component** (`src/components/FileDownload.js`):
     ```javascript
     import { downloadFile } from '../services/api';

     export default function FileDownload({ chatId, file }) {
       const handleDownload = async () => {
         try {
           const token = localStorage.getItem('token');
           const fileData = await downloadFile(token, chatId, file._id);

           const url = window.URL.createObjectURL(new Blob([fileData]));
           const link = document.createElement('a');
           link.href = url;
           link.setAttribute('download', file.filename);
           document.body.appendChild(link);
           link.click();
         } catch (err) {
           console.error('Error downloading file:', err);
         }
       };

       return (
         <div className="mb-2">
           <button onClick={handleDownload} className="bg-green-500 text-white px-4 py-2 rounded">
             Download {file.filename}
           </button>
         </div>
       );
     }
     ```

4. **Chat Window with File Sharing**:
   - **Update Chat Window Component to Include File Upload and Download** (`src/components/ChatWindow.js`):
     ```javascript
     import { useState } from 'react';
     import useChat from '../hooks/useChat';
     import FileUpload from './FileUpload';
     import FileDownload from './FileDownload';

     export default function ChatWindow({ chatId, userId }) {
       const { messages, message, setMessage, sendMessage } = useChat(chatId);
       const [files, setFiles] = useState([]);

       const handleSendMessage = (e) => {
         e.preventDefault();
         sendMessage(userId, message);
       };

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">Chat</h2>
           <div>
             <ul className="mb-5">
               {messages.map((msg, index) => (
                 <li key={index} className="mb-2 p-2 border rounded">
                   <strong>{msg.sender.username}</strong>: {msg.content} <br />
                   <span className="text-gray-500 text-sm">{new Date(msg.timestamp).toLocaleString()}</span>
                 </li>
               ))}
             </ul>
             <form onSubmit={handleSendMessage}>
               <input
                 type="text"
                 value={message}
                 onChange={(e) => setMessage(e.target.value)}
                 className="w-full px-3 py-2 border rounded mb-2"
                 placeholder="Type your message..."
               />
               <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
                 Send
               </button>
             </form>
             <FileUpload chatId={chatId} />
             <div className="mt-4">
               {files.map(file => (
                 <FileDownload key={file._id} chatId={chatId} file={file} />
               ))}
             </div>
           </div>
         </div>
       );
     }
     ```

5. **Usage in a Page**:
   - **Update Page to Include File Sharing** (`src/pages/chat/[chatId].js`):
     ```javascript
     import { useRouter } from 'next/router';
     import ChatWindow from '../../components/ChatWindow';

     export default function ChatPage() {
       const router = useRouter();
       const { chatId } = router.query;
       const userId = 'USER_ID'; // Replace with the actual user ID from authentication

       if (!chatId) {
         return <div>Loading...</div>;
       }

       return <ChatWindow chatId={chatId} userId={userId} />;
     }
     ```

### Conclusion

This setup includes the backend implementation for uploading and downloading files in a chat session with NestJS, and the frontend implementation with Next.js. Users can upload files to a chat session, and download them from the chat session. The `FileUpload` component handles file uploads, and the `FileDownload` component handles file downloads. The `ChatWindow` component is updated to include file upload and download functionalities, ensuring comprehensive file sharing capabilities in your chat application.

### Settings: User Settings Page

#### Frontend Code (Next.js)

1. **Create User Settings Page**:
   - **Create User Settings Page Component** (`src/pages/settings.js`):
     ```javascript
     import { useState, useEffect } from 'react';
     import { getProfile, updateProfile } from '../services/api';

     export default function SettingsPage() {
       const [username, setUsername] = useState('');
       const [notificationPreference, setNotificationPreference] = useState(true);
       const [message, setMessage] = useState('');
       const [error, setError] = useState('');

       useEffect(() => {
         const fetchProfile = async () => {
           try {
             const token = localStorage.getItem('token');
             if (token) {
               const data = await getProfile(token);
               setUsername(data.username);
               setNotificationPreference(data.notificationPreference || true);
             } else {
               setError('No token found');
             }
           } catch (err) {
             setError(err.message);
           }
         };

         fetchProfile();
       }, []);

       const handleSave = async (e) => {
         e.preventDefault();
         try {
           const token = localStorage.getItem('token');
           const updateData = { username, notificationPreference };
           await updateProfile(token, updateData);
           setMessage('Settings updated successfully');
         } catch (err) {
           setError(err.message);
         }
       };

       return (
         <div className="max-w-md mx-auto mt-10">
           <h2 className="text-2xl font-bold mb-5">User Settings</h2>
           {error && <p className="text-red-500">{error}</p>}
           {message && <p className="text-green-500">{message}</p>}
           <form onSubmit={handleSave} className="space-y-4">
             <div>
               <label className="block text-gray-700">Username</label>
               <input
                 type="text"
                 value={username}
                 onChange={(e) => setUsername(e.target.value)}
                 className="w-full px-3 py-2 border rounded"
                 required
               />
             </div>
             <div>
               <label className="block text-gray-700">Notification Preferences</label>
               <select
                 value={notificationPreference}
                 onChange={(e) => setNotificationPreference(e.target.value === 'true')}
                 className="w-full px-3 py-2 border rounded"
               >
                 <option value="true">Enable Notifications</option>
                 <option value="false">Disable Notifications</option>
               </select>
             </div>
             <div>
               <button
                 type="submit"
                 className="w-full px-4 py-2 text-white bg-blue-500 rounded hover:bg-blue-700"
               >
                 Save Settings
               </button>
             </div>
           </form>
         </div>
       );
     }
     ```

2. **API Call for Updating Profile**:
   - **Update API Function for Updating Profile** (`src/services/api.js`):
     ```javascript
     import axios from 'axios';

     const API_URL = 'http://localhost:3000';

     export const register = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/register`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const login = async (username, password) => {
       try {
         const response = await axios.post(`${API_URL}/auth/login`, { username, password });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getProfile = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/auth/me`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const updateProfile = async (token, userData) => {
       try {
         const response = await axios.put(`${API_URL}/auth/me`, userData, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const createChat = async (token, participants) => {
       try {
         const response = await axios.post(`${API_URL}/chats`, { participants }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChats = async (token) => {
       try {
         const response = await axios.get(`${API_URL}/chats`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const getChatDetails = async (token, chatId) => {
       try {
         const response = await axios.get(`${API_URL}/chats/${chatId}`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const sendMessage = async (token, chatId, content) => {
       try {
         const response = await axios.post(`${API_URL}/chats/${chatId}/messages`, { content }, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const uploadFile = async (token, chatId, file) => {
       try {
         const formData = new FormData();
         formData.append('file', file);

         const response = await axios.post(`${API_URL}/chats/${chatId}/files`, formData, {
           headers: {
             Authorization: `Bearer ${token}`,
             'Content-Type': 'multipart/form-data',
           },
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const downloadFile = async (token, chatId, fileId) => {
       try {
         const response = await axios.get(`${API_URL}/chats/${chatId}/files/${fileId}`, {
           headers: {
             Authorization: `Bearer ${token}`,
           },
           responseType: 'blob',
         });
         return response.data;
       } catch (error) {
         throw error.response.data;
       }
     };

     export const logout = () => {
       localStorage.removeItem('token');
       window.location.href = '/login'; // Redirect to login page
     };
     ```

### Conclusion

This setup includes the frontend implementation for a user settings page using Next.js and Tailwind CSS. The user settings page allows users to update their username and notification preferences. The settings are fetched from and updated to the backend using appropriate API calls. This ensures that users can manage their account settings and preferences effectively.

### Future Enhancements

#### 1. Voice and Video Calls

**Description**: Adding support for voice and video calls.

**Implementation Plan**:

1. **WebRTC Integration**:
   - Use WebRTC for real-time communication.
   - Set up signaling server to manage WebRTC connections.

2. **Signaling Server**:
   - Implement signaling server using Socket.io or any other preferred WebSocket library.
   - Handle offer, answer, and ICE candidate exchange.

3. **Frontend UI**:
   - Add buttons for voice and video calls.
   - Create components for managing WebRTC streams (e.g., `<VideoCall />`, `<VoiceCall />`).

4. **Backend API**:
   - Add endpoints for initiating and ending calls.
   - Store call history and status.

**Frontend Code Example**:

```javascript
// src/components/VideoCall.js
import React, { useRef, useEffect, useState } from 'react';
import io from 'socket.io-client';

const VideoCall = ({ userId, chatId }) => {
  const [localStream, setLocalStream] = useState(null);
  const [remoteStream, setRemoteStream] = useState(null);
  const localVideoRef = useRef();
  const remoteVideoRef = useRef();
  const socket = useRef(null);
  const peerConnection = useRef(null);

  useEffect(() => {
    socket.current = io('http://localhost:3000');

    navigator.mediaDevices.getUserMedia({ video: true, audio: true })
      .then(stream => {
        setLocalStream(stream);
        localVideoRef.current.srcObject = stream;
      });

    socket.current.on('offer', handleOffer);
    socket.current.on('answer', handleAnswer);
    socket.current.on('ice-candidate', handleICECandidate);

    return () => {
      socket.current.disconnect();
      if (peerConnection.current) {
        peerConnection.current.close();
      }
    };
  }, []);

  const handleOffer = async (offer) => {
    peerConnection.current = createPeerConnection();
    await peerConnection.current.setRemoteDescription(new RTCSessionDescription(offer));
    const answer = await peerConnection.current.createAnswer();
    await peerConnection.current.setLocalDescription(answer);
    socket.current.emit('answer', { answer, chatId });
  };

  const handleAnswer = async (answer) => {
    await peerConnection.current.setRemoteDescription(new RTCSessionDescription(answer));
  };

  const handleICECandidate = (candidate) => {
    if (peerConnection.current) {
      peerConnection.current.addIceCandidate(new RTCIceCandidate(candidate));
    }
  };

  const createPeerConnection = () => {
    const pc = new RTCPeerConnection();
    pc.onicecandidate = (event) => {
      if (event.candidate) {
        socket.current.emit('ice-candidate', { candidate: event.candidate, chatId });
      }
    };
    pc.ontrack = (event) => {
      setRemoteStream(event.streams[0]);
      remoteVideoRef.current.srcObject = event.streams[0];
    };
    localStream.getTracks().forEach(track => {
      pc.addTrack(track, localStream);
    });
    return pc;
  };

  const startCall = async () => {
    peerConnection.current = createPeerConnection();
    const offer = await peerConnection.current.createOffer();
    await peerConnection.current.setLocalDescription(offer);
    socket.current.emit('offer', { offer, chatId });
  };

  return (
    <div>
      <video ref={localVideoRef} autoPlay muted />
      <video ref={remoteVideoRef} autoPlay />
      <button onClick={startCall}>Start Call</button>
    </div>
  );
};

export default VideoCall;
```

#### 2. Group Chats

**Description**: Adding support for group chat functionality.

**Implementation Plan**:

1. **Database Schema**:
   - Update chat schema to include group chat properties (e.g., group name, members).
   - Modify existing chat schema to distinguish between individual and group chats.

2. **Backend API**:
   - Create endpoints for creating, updating, and deleting group chats.
   - Handle adding and removing members from group chats.

3. **Frontend UI**:
   - Add UI for creating and managing group chats.
   - Update chat list and chat window components to support group chats.

**Backend Code Example**:

```typescript
// src/chat/chat.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Chat, ChatDocument } from '../schemas/chat.schema';
import { CreateGroupChatDto } from './dto/create-group-chat.dto';
import { AddMembersDto } from './dto/add-members.dto';

@Injectable()
export class ChatService {
  constructor(@InjectModel(Chat.name) private chatModel: Model<ChatDocument>) {}

  async createGroupChat(createGroupChatDto: CreateGroupChatDto): Promise<Chat> {
    const newGroupChat = new this.chatModel({
      ...createGroupChatDto,
      isGroupChat: true,
      messages: [],
    });
    return newGroupChat.save();
  }

  async addMembers(chatId: string, addMembersDto: AddMembersDto): Promise<Chat> {
    const chat = await this.chatModel.findById(chatId);
    if (!chat || !chat.isGroupChat) {
      throw new NotFoundException('Group chat not found');
    }
    chat.members.push(...addMembersDto.members);
    return chat.save();
  }
}
```

#### 3. Status Indicators

**Description**: Adding online/offline status indicators for users.

**Implementation Plan**:

1. **Backend WebSocket Integration**:
   - Track user connection and disconnection events.
   - Store user status in a database or in-memory store.

2. **Frontend UI**:
   - Add UI elements to display user status (e.g., green dot for online, gray dot for offline).
   - Update user list and chat components to show status indicators.

**Backend Code Example**:

```typescript
// src/chat/chat.gateway.ts
import {
  WebSocketGateway,
  WebSocketServer,
  OnGatewayInit,
  OnGatewayConnection,
  OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { UserService } from '../user/user.service';

@WebSocketGateway()
export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer() server: Server;

  constructor(private readonly userService: UserService) {}

  afterInit(server: Server) {
    console.log('WebSocket server initialized');
  }

  async handleConnection(client: Socket) {
    const userId = client.handshake.query.userId;
    await this.userService.updateUserStatus(userId, true);
    this.server.emit('userStatus', { userId, status: 'online' });
  }

  async handleDisconnect(client: Socket) {
    const userId = client.handshake.query.userId;
    await this.userService.updateUserStatus(userId, false);
    this.server.emit('userStatus', { userId, status: 'offline' });
  }
}
```

**Frontend Code Example**:

```javascript
// src/components/UserList.js
import { useEffect, useState } from 'react';
import io from 'socket.io-client';

export default function UserList() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    const socket = io('http://localhost:3000');

    socket.on('userStatus', (data) => {
      setUsers((prevUsers) =>
        prevUsers.map((user) =>
          user._id === data.userId ? { ...user, status: data.status } : user
        )
      );
    });

    return () => socket.disconnect();
  }, []);

  return (
    <div>
      <h2 className="text-2xl font-bold mb-5">Users</h2>
      <ul>
        {users.map((user) => (
          <li key={user._id} className="flex items-center mb-2">
            <span
              className={`w-2.5 h-2.5 rounded-full ${
                user.status === 'online' ? 'bg-green-500' : 'bg-gray-500'
              }`}
            ></span>
            <span className="ml-2">{user.username}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}
```

### Conclusion

These enhancements outline the future features of the chat application, including voice and video calls, group chat functionality, and user status indicators. Each enhancement involves both backend and frontend changes to ensure seamless integration and improved user experience.

### Implementing Google Meet or Zoom-like Meeting Feature with Sharable Link

To implement a Google Meet or Zoom-like meeting feature with a sharable link, we can use WebRTC for real-time video and audio communication. Additionally, we can use Socket.io for signaling and managing the connections. Here's a high-level overview and implementation guide:

#### Backend Code (NestJS)

1. **WebSocket Gateway Setup**

- **Install WebSocket Dependencies**:
  ```bash
  npm install @nestjs/websockets @nestjs/platform-socket.io
  ```

- **Create WebSocket Gateway**:
  - **Create WebSocket Gateway** (`src/meeting/meeting.gateway.ts`):
    ```typescript
    import {
      SubscribeMessage,
      WebSocketGateway,
      WebSocketServer,
      OnGatewayInit,
      OnGatewayConnection,
      OnGatewayDisconnect,
    } from '@nestjs/websockets';
    import { Server, Socket } from 'socket.io';

    @WebSocketGateway()
    export class MeetingGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
      @WebSocketServer() server: Server;

      afterInit(server: Server) {
        console.log('WebSocket server initialized');
      }

      handleConnection(client: Socket) {
        console.log(`Client connected: ${client.id}`);
      }

      handleDisconnect(client: Socket) {
        console.log(`Client disconnected: ${client.id}`);
      }

      @SubscribeMessage('joinMeeting')
      handleJoinMeeting(client: Socket, meetingId: string) {
        client.join(meetingId);
        client.broadcast.to(meetingId).emit('userJoined', client.id);
      }

      @SubscribeMessage('signal')
      handleSignal(client: Socket, payload: { meetingId: string; signal: any }) {
        client.broadcast.to(payload.meetingId).emit('signal', {
          userId: client.id,
          signal: payload.signal,
        });
      }
    }
    ```

2. **Meeting Module**:
  - **Create Meeting Module** (`src/meeting/meeting.module.ts`):
    ```typescript
    import { Module } from '@nestjs/common';
    import { MeetingGateway } from './meeting.gateway';

    @Module({
      providers: [MeetingGateway],
    })
    export class MeetingModule {}
    ```

3. **Update App Module**:
  - **Update App Module to Include Meeting Module** (`src/app.module.ts`):
    ```typescript
    import { Module } from '@nestjs/common';
    import { MongooseModule } from '@nestjs/mongoose';
    import { AuthModule } from './auth/auth.module';
    import { ChatModule } from './chat/chat.module';
    import { FileModule } from './file/file.module';
    import { MeetingModule } from './meeting/meeting.module';

    @Module({
      imports: [
        MongooseModule.forRoot('mongodb://localhost/chat-app'),
        AuthModule,
        ChatModule,
        FileModule,
        MeetingModule,
      ],
    })
    export class AppModule {}
    ```

#### Frontend Code (Next.js)

1. **Install Socket.io Client**:
  ```bash
  npm install socket.io-client
  ```

2. **WebRTC Setup**:
  - **Create WebRTC Hook** (`src/hooks/useWebRTC.js`):
    ```javascript
    import { useEffect, useRef, useState } from 'react';
    import io from 'socket.io-client';

    const useWebRTC = (meetingId) => {
      const [remoteStreams, setRemoteStreams] = useState([]);
      const localStream = useRef(null);
      const socket = useRef(null);
      const peerConnections = useRef({});

      useEffect(() => {
        socket.current = io('http://localhost:3000');

        navigator.mediaDevices.getUserMedia({ video: true, audio: true })
          .then(stream => {
            localStream.current.srcObject = stream;

            socket.current.emit('joinMeeting', meetingId);

            socket.current.on('userJoined', userId => {
              const peerConnection = createPeerConnection(userId);
              peerConnections.current[userId] = peerConnection;
              stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
            });

            socket.current.on('signal', async ({ userId, signal }) => {
              if (peerConnections.current[userId]) {
                await peerConnections.current[userId].setRemoteDescription(new RTCSessionDescription(signal));
                if (signal.type === 'offer') {
                  const answer = await peerConnections.current[userId].createAnswer();
                  await peerConnections.current[userId].setLocalDescription(answer);
                  socket.current.emit('signal', { meetingId, signal: answer });
                }
              } else {
                const peerConnection = createPeerConnection(userId);
                await peerConnection.setRemoteDescription(new RTCSessionDescription(signal));
                peerConnections.current[userId] = peerConnection;
                if (signal.type === 'offer') {
                  const answer = await peerConnection.createAnswer();
                  await peerConnection.setLocalDescription(answer);
                  socket.current.emit('signal', { meetingId, signal: answer });
                }
              }
            });
          });

        return () => {
          Object.values(peerConnections.current).forEach(pc => pc.close());
          socket.current.disconnect();
        };
      }, [meetingId]);

      const createPeerConnection = (userId) => {
        const pc = new RTCPeerConnection();
        pc.onicecandidate = (event) => {
          if (event.candidate) {
            socket.current.emit('signal', { meetingId, signal: event.candidate });
          }
        };
        pc.ontrack = (event) => {
          setRemoteStreams((prevStreams) => {
            const existingStream = prevStreams.find(stream => stream.id === event.streams[0].id);
            if (existingStream) return prevStreams;
            return [...prevStreams, event.streams[0]];
          });
        };
        return pc;
      };

      return { localStream, remoteStreams };
    };

    export default useWebRTC;
    ```

3. **Meeting Component**:
  - **Create Meeting Component** (`src/components/Meeting.js`):
    ```javascript
    import React, { useRef } from 'react';
    import useWebRTC from '../hooks/useWebRTC';

    const Meeting = ({ meetingId }) => {
      const { localStream, remoteStreams } = useWebRTC(meetingId);
      const localVideoRef = useRef();

      useEffect(() => {
        if (localStream.current) {
          localVideoRef.current.srcObject = localStream.current.srcObject;
        }
      }, [localStream]);

      return (
        <div>
          <video ref={localVideoRef} autoPlay muted className="local-video" />
          <div className="remote-videos">
            {remoteStreams.map((stream, index) => (
              <video key={index} autoPlay className="remote-video" ref={video => {
                if (video) {
                  video.srcObject = stream;
                }
              }} />
            ))}
          </div>
        </div>
      );
    };

    export default Meeting;
    ```

4. **Usage in a Page**:
  - **Create Meeting Page** (`src/pages/meeting/[meetingId].js`):
    ```javascript
    import { useRouter } from 'next/router';
    import Meeting from '../../components/Meeting';

    export default function MeetingPage() {
      const router = useRouter();
      const { meetingId } = router.query;

      if (!meetingId) {
        return <div>Loading...</div>;
      }

      return <Meeting meetingId={meetingId} />;
    }
    ```

### Conclusion

This setup provides a basic implementation of a Google Meet or Zoom-like meeting feature with a sharable link using WebRTC for real-time communication and Socket.io for signaling. The backend uses NestJS to manage WebSocket connections, and the frontend uses Next.js to handle the video call UI and WebRTC interactions. This implementation can be further extended with additional features such as screen sharing, chat, and more.

### Adding Screen Sharing Feature

To add a screen sharing feature, we can use the `getDisplayMedia` method from the WebRTC API. This allows users to share their screen during the video call.

#### Frontend Code (Next.js)

1. **Update WebRTC Hook**:
   - **Enhance the `useWebRTC` hook to include screen sharing functionality** (`src/hooks/useWebRTC.js`):
     ```javascript
     import { useEffect, useRef, useState } from 'react';
     import io from 'socket.io-client';

     const useWebRTC = (meetingId) => {
       const [remoteStreams, setRemoteStreams] = useState([]);
       const localStream = useRef(null);
       const screenStream = useRef(null);
       const socket = useRef(null);
       const peerConnections = useRef({});

       useEffect(() => {
         socket.current = io('http://localhost:3000');

         navigator.mediaDevices.getUserMedia({ video: true, audio: true })
           .then(stream => {
             localStream.current = stream;
             attachStreamToVideo(localStream.current, 'local-video');
             
             socket.current.emit('joinMeeting', meetingId);

             socket.current.on('userJoined', userId => {
               const peerConnection = createPeerConnection(userId);
               peerConnections.current[userId] = peerConnection;
               stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
             });

             socket.current.on('signal', async ({ userId, signal }) => {
               if (peerConnections.current[userId]) {
                 await peerConnections.current[userId].setRemoteDescription(new RTCSessionDescription(signal));
                 if (signal.type === 'offer') {
                   const answer = await peerConnections.current[userId].createAnswer();
                   await peerConnections.current[userId].setLocalDescription(answer);
                   socket.current.emit('signal', { meetingId, signal: answer });
                 }
               } else {
                 const peerConnection = createPeerConnection(userId);
                 await peerConnection.setRemoteDescription(new RTCSessionDescription(signal));
                 peerConnections.current[userId] = peerConnection;
                 if (signal.type === 'offer') {
                   const answer = await peerConnection.createAnswer();
                   await peerConnection.setLocalDescription(answer);
                   socket.current.emit('signal', { meetingId, signal: answer });
                 }
               }
             });
           });

         return () => {
           Object.values(peerConnections.current).forEach(pc => pc.close());
           socket.current.disconnect();
         };
       }, [meetingId]);

       const createPeerConnection = (userId) => {
         const pc = new RTCPeerConnection();
         pc.onicecandidate = (event) => {
           if (event.candidate) {
             socket.current.emit('signal', { meetingId, signal: event.candidate });
           }
         };
         pc.ontrack = (event) => {
           setRemoteStreams((prevStreams) => {
             const existingStream = prevStreams.find(stream => stream.id === event.streams[0].id);
             if (existingStream) return prevStreams;
             return [...prevStreams, event.streams[0]];
           });
         };
         return pc;
       };

       const attachStreamToVideo = (stream, videoId) => {
         const videoElement = document.getElementById(videoId);
         if (videoElement) {
           videoElement.srcObject = stream;
         }
       };

       const startScreenSharing = async () => {
         try {
           screenStream.current = await navigator.mediaDevices.getDisplayMedia({ video: true });
           const screenTrack = screenStream.current.getVideoTracks()[0];
           
           Object.values(peerConnections.current).forEach(pc => {
             const sender = pc.getSenders().find(s => s.track.kind === 'video');
             if (sender) {
               sender.replaceTrack(screenTrack);
             }
           });
           
           screenTrack.onended = () => {
             stopScreenSharing();
           };
         } catch (error) {
           console.error('Error starting screen sharing:', error);
         }
       };

       const stopScreenSharing = () => {
         const videoTrack = localStream.current.getVideoTracks()[0];
         Object.values(peerConnections.current).forEach(pc => {
           const sender = pc.getSenders().find(s => s.track.kind === 'video');
           if (sender) {
             sender.replaceTrack(videoTrack);
           }
         });
         screenStream.current.getTracks().forEach(track => track.stop());
         screenStream.current = null;
       };

       return { localStream, remoteStreams, startScreenSharing, stopScreenSharing };
     };

     export default useWebRTC;
     ```

2. **Update Meeting Component**:
   - **Update the `Meeting` component to include screen sharing buttons** (`src/components/Meeting.js`):
     ```javascript
     import React, { useRef, useEffect } from 'react';
     import useWebRTC from '../hooks/useWebRTC';

     const Meeting = ({ meetingId }) => {
       const { localStream, remoteStreams, startScreenSharing, stopScreenSharing } = useWebRTC(meetingId);
       const localVideoRef = useRef();

       useEffect(() => {
         if (localStream.current) {
           localVideoRef.current.srcObject = localStream.current.srcObject;
         }
       }, [localStream]);

       return (
         <div>
           <video ref={localVideoRef} autoPlay muted id="local-video" className="local-video" />
           <div className="remote-videos">
             {remoteStreams.map((stream, index) => (
               <video key={index} autoPlay className="remote-video" ref={video => {
                 if (video) {
                   video.srcObject = stream;
                 }
               }} />
             ))}
           </div>
           <button onClick={startScreenSharing} className="bg-blue-500 text-white px-4 py-2 rounded">Share Screen</button>
           <button onClick={stopScreenSharing} className="bg-red-500 text-white px-4 py-2 rounded">Stop Sharing</button>
         </div>
       );
     };

     export default Meeting;
     ```

3. **Usage in a Page**:
   - **Ensure the meeting page uses the updated `Meeting` component** (`src/pages/meeting/[meetingId].js`):
     ```javascript
     import { useRouter } from 'next/router';
     import Meeting from '../../components/Meeting';

     export default function MeetingPage() {
       const router = useRouter();
       const { meetingId } = router.query;

       if (!meetingId) {
         return <div>Loading...</div>;
       }

       return <Meeting meetingId={meetingId} />;
     }
     ```

### Backend Code (NestJS)

1. **No Change Required for Backend**:
   - The existing WebSocket gateway setup remains the same, as the signaling process for screen sharing is handled similarly to regular video/audio streams. The `signal` event will carry the necessary signaling data for screen sharing as well.

### Conclusion

This setup includes the frontend implementation for a screen sharing feature using WebRTC and Next.js. The `useWebRTC` hook is enhanced to manage screen sharing, allowing users to start and stop screen sharing during a video call. The `Meeting` component includes buttons to control screen sharing. This feature enhances the overall meeting experience by enabling users to share their screens seamlessly.

Disclaimer: This content is generated by AI.