Creating a Pinterest Clone: A Comprehensive Full-Stack Guide Using Node.js and MongoDB


Author: Tajamul
Published on: Hashnode
Project Repo: GitHub
Introduction
Pinterest is a visually captivating platform where users can discover, save, and share images. Inspired by its functionality, I embarked on creating a Pinterest Clone using Node.js, Express.js, MongoDB, and EJS. This blog provides a comprehensive walkthrough of the development process, highlights the key technologies used, and discusses the challenges encountered along the way.
Tech Stack & Tools
Before we dive into the code, let’s look at the stack used in this project:
✅ Backend: Node.js, Express.js, MongoDB
✅ Authentication: Passport.js (passport-local, passport-local-mongoose)
✅ File Uploads: Multer
✅ Session Management: Express-session
✅ Frontend: EJS templates, custom CSS
📌 Final UI of the Pinterest Clone
"Here is a screenshot of the completed Pinterest Clone interface. The design closely mimics Pinterest’s grid-based layout, where users can browse and upload images seamlessly."
1️⃣ Setting Up the Backend
To start, initialize a Node.js project and install the required dependencies:
mkdir pinterest-clone && cd pinterest-clone
npm init -y
npm install express mongoose ejs multer passport passport-local passport-local-mongoose express-session
Create an app.js file and set up a basic Express server:
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const expressSession = require('express-session');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
const passport = require('passport');
var app = express();
app.use(expressSession({
resave: false,
saveUninitialized: false,
secret: "pinsecret",
}))
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(usersRouter.serializeUser());
passport.deserializeUser(usersRouter.deserializeUser());
📜 Understanding the
app.js
FileThe
app.js
file serves as the backbone of the Pinterest Clone project. It initializes the Express application, sets up middleware, configures Passport.js for authentication, and defines how the app handles requests and errors.Here’s a breakdown of the key parts:
🔹 Imports Required Modules – Express, Passport.js, Session management, and others.
🔹 Session & Authentication Setup – Configures Express sessions and initializes Passport.js for user authentication.
🔹 View Engine Configuration – Sets up EJS as the templating engine.
🔹 Middleware Usage – Includes body parsers, logging (morgan
), and static file serving.
🔹 Routes Configuration – Defines the main routes (indexRouter
andusersRouter
).
🔹 Error Handling – Catches 404 errors and general errors to display an appropriate response."This screenshot shows the backend server running on Node.js. The Express server is successfully connected to MongoDB, and the application is ready to handle requests."
🛠️ Understanding the index.js
(Routing File)
The index.js
file in the routes
directory is responsible for handling routes, authentication, and user interactions in our Pinterest Clone. It works with Express, Passport.js, and MongoDB to manage user registration, login, logout, and posting images.
Key Features of index.js
✅ User Authentication: Uses Passport.js to handle login and session management.
✅ Post Creation & Image Upload: Uses Multer to upload images and store them in MongoDB.
✅ Protected Routes: Ensures users can only access certain pages when logged in (isLoggedIn
middleware).
✅ Profile & Feed Pages: Fetches user data and dynamically populates pages like profile, feed, and post views.
🚀 Code Breakdown
1️⃣ Home Page Route (/
) – Renders the main page.
2️⃣ User Registration (/register
) – Saves user details in MongoDB and encrypts passwords.
3️⃣ Login & Logout (/login
& /logout
) – Uses Passport.js to authenticate users.
4️⃣ Profile Page (/profile
) – Fetches user data along with uploaded posts.
5️⃣ Image Upload (/fileupload
& /createpost
) – Uses Multer to handle file uploads.
6️⃣ Feed Page (/feed
) – Displays posts from all users.
📌 Setting Up Routes in index.js
(Briefly explain that index.js
is the main routing file handling user authentication, profile management, and posts.)
1️⃣ Importing Required Modules
var express = require('express');
var router = express.Router();
const userModel = require("./users");
const passport = require('passport');
const upload = require('./multer');
const postModel = require("./post");
➡️ Here, we import Express for routing, passport
for authentication, multer
for image uploads, and MongoDB models (userModel
, postModel
).
2️⃣ Setting Up Passport.js Authentication
const localStrategy = require('passport-local');
passport.use(new localStrategy(userModel.authenticate()));
➡️ This configures Passport.js to use the userModel
for authentication.
3️⃣ User Routes
Home Page
router.get('/', function(req, res) {
res.render('index');
});
➡️ Renders the main page when users visit the website.
User Registration
router.post('/register', function(req, res) {
const data = new userModel({
name: req.body.fullname,
username: req.body.username,
email: req.body.email,
contact: req.body.contact
});
userModel.register(data, req.body.password)
.then(function() {
passport.authenticate("local")(req, res, function() {
res.redirect("/profile");
});
});
});
➡️ Registers a new user, encrypts their password, and redirects them to their profile.
Register Page
"This is the Register page where users enter their credentials to create their profile."
4️⃣ User Authentication
Login Route
router.post("/login", passport.authenticate("local", {
successRedirect: "/profile",
failureRedirect: "/"
}));
Login Page
"This is the login page where users enter their credentials to access their profile."
➡️ Authenticates users using Passport.js and redirects them upon success or failure.
Logout Route
router.get("/logout", function(req, res) {
req.logout(function(err) {
if (err) { return next(err); }
res.redirect("/");
});
});
➡️ Logs out users and redirects them to the home page.
5️⃣ Handling Image Uploads with Multer
Uploading a Profile Picture
router.post('/fileupload', upload.single("image"), async function(req, res) {
const user = await userModel.findOne({username: req.session.passport.user});
user.profileImage = req.file.filename;
await user.save();
res.redirect("/profile");
});
➡️ Uses Multer to upload a profile picture and updates the user’s profile.
4️⃣ Displaying Images in a Pinterest-Style Grid
To mimic Pinterest’s layout, use EJS and CSS Grid for styling.
EJS Template (views/profile.ejs):
<% user.posts.forEach(function(elem, index){ %>
<%
// Assign different widths and heights for variety
let widthClass = index % 3 === 0 ? 'w-80' : index % 3 === 1 ? 'w-60' : 'w-72';
let heightClass = index % 2 === 0 ? 'h-96' : 'h-72';
%>
<div class="card <%= widthClass %>">
<div class="w-full <%= heightClass %> bg-zinc-200 rounded-lg overflow-hidden">
<img class="w-full h-full object-cover" src="/images/uploads/<%= elem.image %>" alt="">
</div>
<h5 href="/show/posts" class="inline-block text-xl font-medium opacity-50 mt-3">
<%= elem.title %>
</h5>
</div>
<% }) %>
Successful Login (Profile Page)
"After logging in, users are redirected to their profile page, where they can view and manage their posts."
5️⃣ 🚀 Deploying the Pinterest Clone on Render
Once the project is ready, deploying it ensures that your application is live and accessible from anywhere. For this project, we used Render, a free and developer-friendly hosting service for full-stack applications.
🔧 Steps to Deploy on Render
1️⃣ Push the Code to GitHub
Before deploying, ensure your code is pushed to GitHub:
bashCopyEditgit init
git add .
git commit -m "Initial Commit"
git branch -M main
git remote add origin https://github.com/yourusername/pinterest-clone.git
git push -u origin main
2️⃣ Set Up a Render Web Service
Go to Render and sign up or log in.
Click "New +" → "Web Service".
Select your GitHub repository (
pinterest-clone
).Choose a runtime (Node.js).
Set the build command:
npm install
Set the start command:
node app.js
Click "Create Web Service", and Render will deploy your app.
3️⃣ Configure the Environment Variables
Go to the "Environment" section in Render and add:
MONGO_URI = your_mongodb_atlas_connection_string
SESSION_SECRET = your_secret_key
Save the changes and redeploy the service if needed.
4️⃣ Test Your Live App
Once the deployment is complete, Render provides a live URL (e.g., https://pinterest-clone.onrender.com
).
Visit the link to test your Pinterest Clone in production! 🎉
🏆 Conclusion
Building a Pinterest Clone using Node.js, Express, and MongoDB was an incredible experience in full-stack development. This project gave hands-on experience with:
✅ User authentication (Passport.js)
✅ File uploads (Multer)
✅ Session management (Express-Session)
✅ Database integration (MongoDB & Mongoose)
With Render, we successfully deployed the project, making it accessible online.
💡 What’s Next?
Enhance the UI with React.js for a dynamic frontend.
Use Cloudinary for optimized image storage.
Implement infinite scrolling for better UX.
Adding other pages.
User delete post option and other things
💬 Got questions or suggestions? Drop a comment below!
🚀 Check out the full source code on GitHub: 🔗 GitHub Repository
Subscribe to my newsletter
Read articles from Tajamul directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Tajamul
Tajamul
👨💻 Full Stack Developer | Building epic clones & futuristic projects 🚀 | MERN Stack Enthusiast | Sharing dev tips, bugs, and breakthroughs 💻 | Code. Learn. Repeat. | Follow my journey! ✨