In this project, we will be building a real-time chat application with Node.js, Express, Socket.io, and MongoDB.
Let's take a look at what you'll need first:
- Editor: You can use VS Code for this.
- Node.js: JavaScript runtime environment.
- Database: We will be using MongoDB in this case.
- JavaScript: A basic understanding of JavaScript is required.
Now take a cup of coffee and get ready for your real time project. First open your VS code. Press
Ctrl + J
(Open your terminal) and enter the following command.
// Create Two directory one for frontend and one for backend.
mkdir chat.frontend
mkdir chat.backend
Now Open two terminal one for backend and one for frontend. and initialize Nodejs Directory.
npm init
You’ll be prompted to fill in some information — that’s okay. The information will be used to set up your package.json file.
Dependencies Installation For Backend
npm install express socket.io mongoose cors
Now Create Server.js file in the chat.backend folder make sure you install the above dependencies in chat.backend folder.
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const mongoose = require('mongoose');
const cors = require('cors');
- express: Web framework for Node.js
- socket.io: Real-time communication library
- mongoose: MongoDB ODM(Object Data Modeling) for Node.js
- cors: Middleware for handling Cross-Origin Resource Sharing (CORS)
// Create an Express app and a HTTP server
const app = express();
const server = http.createServer(app);
// Create a Socket.io instance with CORS options
const io = socketIo(server, {
cors: {
origin: 'http://localhost:3000',
// Frontend URL (React running here)
methods: ['GET', 'POST'],
allowedHeaders: ['my-custom-header'],
// Optional headers if needed
credentials: true
// If you need to handle cookies or authentication
}
});
Things to Remember: If your frontend is on another port and backend is on another. It is very essential to config
the cors setting in socketIO otherwise you will always get CORS error. This is config in both frontend an backend too.
Middlewares
app.use(cors());
app.use(express.json());
// for parsing request body.
Mongodb Database Connection
mongoose.connect(process.env.MONGO_URI)
.then(() => console.log('Connected to MongoDB'))
.catch((err) => console.error('Failed to connect to MongoDB', err));
Simple Schema for Saving Messages
// Message Schema for MongoDB
const messageSchema = new mongoose.Schema({
sender: String,
content: String,
timestamp: { type: Date, default: Date.now },
});
const Message = mongoose.model('Message', messageSchema);
Setup Routes
// Serve a simple route for testing
app.get('/', (req, res) => {
res.send('Real-time Chat Backend');
});
// Set up Socket.io for real-time communication
io.on('connection', (socket) => {
console.log('A user connected');
// Listen for chat messages
socket.on('chat-message', async (msg) => {
// Save the message to MongoDB
const newMessage = new Message({
sender: 'User',
// You can use actual user names or authentication
content: msg,
});
await newMessage.save();
// Emit the message to all connected clients
io.emit('chat-message', msg);
});
// Handle disconnection
socket.on('disconnect', () => {
console.log('A user disconnected');
});
});
Note: This is a simple project for a basic understanding of how to develop a real-time chat application. If you are looking to create something like WhatsApp, refer to this GitHub repo
Start the server on port 5000
const PORT = process.env.PORT || 5000;
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Frontend(React) Configuration
import React, { useState, useEffect } from 'react';
import io from 'socket.io-client';
// Connect to the backend server's Socket.io endpoint
const socket = io('http://localhost:5000');
// Make sure this matches your backend URL
const Chat = () => {
const [message, setMessage] = useState('');
const [messages, setMessages] = useState([]);
useEffect(() => {
// Listen for incoming chat messages
socket.on('chat-message', (msg) => {
setMessages((prevMessages) => [...prevMessages, msg]);
});
return () => {
socket.off('chat-message');
};
}, []);
const sendMessage = (e) => {
e.preventDefault();
if (message.trim()) {
socket.emit('chat-message', message);
// Emit message to the backend
setMessage('');
}
};
return (
<div>
<h1>Real-Time Chat</h1>
<div id="chat-window" style={{ height: '300px', overflowY: 'scroll' }}>
{messages.map((msg, index) => (
<div key={index}>{msg}</div>
))}
</div>
<form onSubmit={sendMessage}>
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Type a message"
/>
<button type="submit">Send</button>
</form>
</div>
);
}
export default Chat;
Note
Make sure to config the backend port correctly in io
If you're thinking of building something that
involves a number of users, then you need to configure an authentication system for them and implement more advanced
data modeling, as shown below
Data Schema That Involve number of Users.
User Model
// models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs'); // For password hashing
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
trim: true
},
email: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true
},
password: {
type: String,
required: true,
},
createdAt: {
type: Date,
default: Date.now
}
});
// Hash the password before saving the user
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
try {
this.password = await bcrypt.hash(this.password, 10);
next();
} catch (err) {
next(err);
}
});
// Compare the entered password with the hashed one
userSchema.methods.comparePassword = function(password) {
return bcrypt.compare(password, this.password);
};
const User = mongoose.model('User', userSchema);
module.exports = User;
Conversation Model:
Here you can store the Conversation Held between Users, and You can facilate the group Conversation too.
// models/Conversation.js
const mongoose = require('mongoose');
const conversationSchema = new mongoose.Schema({
participants: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
}],
createdAt: {
type: Date,
default: Date.now
},
lastMessage: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Message'
}
});
// To get the participants of a conversation
conversationSchema.methods.getParticipants = function() {
return this.participants.map(participant => participant.username);
};
const Conversation = mongoose.model('Conversation',conversationSchema);
module.exports = Conversation;
Message Model
With this Message model, you can save the message with a reference to the conversation ID. This way, you'll have a better understanding of which users are communicating through this message.
// models/Message.js
const mongoose = require('mongoose');
const messageSchema = new mongoose.Schema({
conversation: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Conversation',
required: true
},
sender: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
content: {
type: String,
required: true,
trim: true
},
timestamp: {
type: Date,
default: Date.now
}
});
const Message = mongoose.model('Message', messageSchema);
module.exports = Message;
Key Points to Remember
- In this project above, I have shown you only the basic chat application functionality. If you're looking to build something like WhatsApp, you can refer to this.Frontend, Backend.
- If you encounter any difficulty in setting up the code, I am always here to help you out. Just leave a message below.
- If you're looking to create a real-time project and need any help, please feel free to reach out to me.