Building a ChatGPT Clone with MERN: My Step-by-Step Journey


Why I Built This Clone
When I first started exploring AI integration, I didn’t want to just read about it — I wanted to build something real. And what better challenge than creating a ChatGPT-style clone using the MERN stack?
This project became the perfect playground: I got to apply full-stack fundamentals, integrate OpenAI APIs, and even practice Docker deployment. Along the way, I made plenty of mistakes (and learned from them), but the end result was a working clone that recruiters and peers found impressive.
Tech Stack
Here’s what powered my clone:
Frontend: React (hooks, fetch API).
Backend: Node.js + Express.
Database: MongoDB (for saving chat history).
AI: OpenAI API (chat completions).
Deployment: Docker + GitHub Actions (CI/CD).
Step 1: Setting Up the Backend
I started by creating a simple Express server to handle chat requests.
// server.js (Node.js backend for ChatGPT Clone)
// Import required libraries
import express from "express";
import fetch from "node-fetch";
import dotenv from "dotenv";
dotenv.config();
const app = express();
app.use(express.json());
// Chat route
app.post("/api/chat", async (req, res) => {
try {
const { message } = req.body;
// Call OpenAI API
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.OPENAI_API_KEY}`, // Secure API key
},
body: JSON.stringify({
model: "gpt-4",
messages: [{ role: "user", content: message }],
}),
});
const data = await response.json();
res.json({ reply: data.choices[0].message.content });
} catch (error) {
console.error("Error:", error);
res.status(500).json({ error: "Chat request failed" });
}
});
app.listen(5000, () => console.log("Server running on port 5000"));
Lesson: API keys must always be secured in .env
files, never exposed in frontend.
Step 2: Creating the React Chat UI
Next, I made a chat interface in React.
// ChatBox.jsx (React frontend)
// Import React
import React, { useState } from "react";
function ChatBox() {
const [input, setInput] = useState("");
const [messages, setMessages] = useState([]);
// Handle user input submission
const handleSend = async (e) => {
e.preventDefault();
// Add user message to chat
setMessages([...messages, { sender: "user", text: input }]);
// Send message to backend
const res = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: input }),
});
const data = await res.json();
// Add AI response to chat
setMessages((prev) => [...prev, { sender: "ai", text: data.reply }]);
setInput("");
};
return (
<div>
<div>
{messages.map((msg, idx) => (
<p key={idx}>
<strong>{msg.sender}:</strong> {msg.text}
</p>
))}
</div>
<form onSubmit={handleSend}>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type your message..."
/>
<button type="submit">Send</button>
</form>
</div>
);
}
export default ChatBox;
Lesson: Even a simple UI can feel powerful when connected to AI.
Step 3: Adding Docker + CI/CD
Instead of keeping this local, I dockerized the app:
# Dockerfile
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5000
CMD ["npm", "start"]
Then, I set up a GitHub Actions workflow for automatic builds and deployments.
Lesson: Recruiters value developers who know DevOps basics.
Challenges I Faced
OpenAI rate limits — fixed by adding exponential backoff retries.
Long responses breaking UI — fixed with scrollable chat window.
API cost awareness — added logging to MongoDB to track token usage.
Key Takeaways
Full-stack + AI is not just hype — it’s production reality.
Building this clone forced me to think like a real engineer: security, error handling, scalability.
Docker + CI/CD elevated the project from “student demo” to production-ready.
What’s Next
In my next article, I’ll dive into Dockerizing a Full-Stack App: Why It Matters for Real Jobs — because containerization is what makes projects truly deployable at scale.
Subscribe to my newsletter
Read articles from Harsimarpreet Singh Sahota directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Harsimarpreet Singh Sahota
Harsimarpreet Singh Sahota
Aspiring Software Engineer/Developer with a robust foundation in 𝑫𝒂𝒕𝒂 𝑺𝒕𝒓𝒖𝒄𝒕𝒖𝒓𝒆𝒔 & 𝑨𝒍𝒈𝒐𝒓𝒊𝒕𝒉𝒎𝒔 and 𝑭𝒖𝒍𝒍 𝑺𝒕𝒂𝒄𝒌 𝑾𝒆𝒃 𝑫𝒆𝒗𝒆𝒍𝒐𝒑𝒎𝒆𝒏𝒕 specializing in MERN stack technologies , actively seeking opportunities to contribute my skills and grow professionally in software engineering and development roles. 𝐏𝐫𝐨𝐟𝐢𝐜𝐢𝐞𝐧𝐭 𝐢𝐧 : Skilled in HTML, CSS, JavaScript, Bootstrap, Tailwind, Express.js, Node.js, SQL, MongoDB, React.js, Material-UI, Java, Data Structures & Algorithms, Git, GitHub, and VS Code. Experienced in frontend and backend development, including API development, templating, software deployment, and database management. Proficient in managing state and events in React, optimizing database performance, and effective team collaboration. 𝐂𝐮𝐫𝐫𝐞𝐧𝐭 𝐫𝐨𝐥𝐞 : Student at the Southern Alberta Institute of Technology. Recognized as "𝑻𝒐𝒑 𝒊𝒏 𝑪𝒐𝒖𝒏𝒕𝒓𝒚" by Queen Rania of Jordan . Continually expanding my knowledge and skills in software development. 𝐀𝐜𝐚𝐝𝐞𝐦𝐢𝐜 𝐄𝐱𝐜𝐞𝐥𝐥𝐞𝐧𝐜𝐞 : Achieved 92% in 12th grade (non-medical) and consistently excel in my current studies .I played chess at the 𝙨𝙩𝙖𝙩𝙚 𝙡𝙚𝙫𝙚𝙡, proudly representing Punjab, demonstrating strategic thinking, problem-solving, and a disciplined approach—all of which I bring to my software development career. 𝐍𝐨𝐭𝐚𝐛𝐥𝐞 𝐀𝐜𝐡𝐢𝐞𝐯𝐞𝐦𝐞𝐧𝐭𝐬 : Ex-Software Developer at Broadways Enterprises. Completed 𝐢𝐧𝐭𝐞𝐫𝐧𝐬𝐡𝐢𝐩𝐬 in full stack web development at CodSoft and CodeAlpha. 𝐅𝐨𝐮𝐧𝐝𝐞𝐫 and president of Tech Career Society dedicated to empowering tech careers and bridging the gap between talent and opportunity.