Learn step-by-step how to build a powerful online auction platform using Replit, from coding basics to deployment and user-friendly features

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
To build an auction platform on Replit, set up a Node.js + Express backend, an HTML/JavaScript (or React) frontend, and a persistent database (like Replit’s built-in database or an external one such as MongoDB Atlas). The backend handles bids, item listings, and timing; the frontend displays auction items and updates bids live via WebSocket or polling. You’ll use the Secrets tab for API keys (never hardcode them), and store auction state in a real database. Replit can host everything inside one Repl — ideal for small or demo-sized platforms.
In Replit, click Create Repl → choose Node.js. This gives you a index.js file to start your backend. Install Express:
npm install express
Now, open index.js and add this server code:
// index.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
app.use(express.static('public')); // serve frontend files from public folder
let auctions = [
{id: 1, item: "Vintage Clock", currentBid: 50, highestBidder: null}
];
// Route to get all auctions
app.get('/api/auctions', (req, res) => {
res.json(auctions);
});
// Route to place a bid
app.post('/api/bid', (req, res) => {
const {auctionId, bidder, amount} = req.body;
const auction = auctions.find(a => a.id === auctionId);
if (!auction) return res.status(404).json({message: "Auction not found"});
if (amount <= auction.currentBid) return res.status(400).json({message: "Bid too low"});
auction.currentBid = amount;
auction.highestBidder = bidder;
res.json({message: "Bid accepted", auction});
});
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
This backend handles listing auctions and accepting bids. Keep it simple for now—later you can store auctions in a real database instead of in-memory objects.
In the Replit file tree, create a new folder named public. Inside it, create a new file index.html and another called main.js.
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Auction Platform</title>
</head>
<body>
<h2>Live Auctions</h2>
<div id="auction-list"></div>
<h3>Place a Bid</h3>
<input id="auctionId" placeholder="Auction ID" />
<input id="bidder" placeholder="Your Name" />
<input id="amount" placeholder="Bid Amount" />
<button id="placeBid">Place Bid</button>
<script src="main.js"></script>
</body>
</html>
// public/main.js
async function loadAuctions() {
const res = await fetch('/api/auctions');
const data = await res.json();
const container = document.getElementById('auction-list');
container.innerHTML = data.map(a => `
<div>
<b>${a.item}</b> — Current bid: $${a.currentBid} (${a.highestBidder || "no bidder yet"})
</div>
`).join('');
}
document.getElementById('placeBid').addEventListener('click', async () => {
const auctionId = Number(document.getElementById('auctionId').value);
const bidder = document.getElementById('bidder').value;
const amount = Number(document.getElementById('amount').value);
const res = await fetch('/api/bid', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({auctionId, bidder, amount})
});
const result = await res.json();
alert(result.message);
loadAuctions(); // refresh after bid
});
loadAuctions();
setInterval(loadAuctions, 5000); // refresh every 5 seconds to simulate live updates
For persistent data beyond runtime, use Replit Database by installing the @replit/database package:
npm install @replit/database
Then update your index.js with real database storage — right after the existing imports:
const Database = require("@replit/database");
const db = new Database();
Change your routes to interact with the DB like this:
app.get('/api/auctions', async (req, res) => {
const auctions = await db.get("auctions") || [];
res.json(auctions);
});
app.post('/api/bid', async (req, res) => {
const {auctionId, bidder, amount} = req.body;
const auctions = await db.get("auctions") || [];
const auction = auctions.find(a => a.id === auctionId);
if (!auction) return res.status(404).json({message: "Auction not found"});
if (amount <= auction.currentBid) return res.status(400).json({message: "Bid too low"});
auction.currentBid = amount;
auction.highestBidder = bidder;
await db.set("auctions", auctions);
res.json({message: "Bid accepted", auction});
});
In Replit, go to the left sidebar → Secrets (Lock icon). Add your secret keys, like database URLs or API tokens (if using external DB). You can access them using process.env.YOUR\_KEY inside index.js. This keeps your keys hidden from the public Repl view.
Click the Run button in Replit. It’ll start the Express server and automatically host the frontend. Your complete project lives within one Repl: the backend in index.js and the frontend inside the public folder. You can open the generated URL to use your auction system live. Replit’s multiplayer lets team members edit or debug in real-time.
/api/bid) respond correctly outside the editor preview.
That’s a fully working foundation for an auction platform on Replit — real, simple, and expandable. You can later enhance it with authentication, real-time updates using Socket.IO, or migrate to an external database for scaling.
<script type="module">
import express from "express";
import { Server } from "socket.io";
import http from "http";
import Database from "@replit/database";
const app = express();
const server = http.createServer(app);
const io = new Server(server);
const db = new Database();
app.use(express.json());
// structure: auction:{id} = { item, bids: [{user, amount, time}] , endsAt }
async function placeBid(auctionId, user, amount) {
const auction = (await db.get(`auction:${auctionId}`)) || null;
if (!auction) throw new Error("Auction not found");
const currentHighest = auction.bids?.[auction.bids.length - 1]?.amount || 0;
if (Date.now() > auction.endsAt) throw new Error("Auction closed");
if (amount <= currentHighest) throw new Error("Bid too low");
const newBid = { user, amount, time: Date.now() };
auction.bids.push(newBid);
await db.set(`auction:${auctionId}`, auction);
io.to(auctionId).emit("newBid", newBid);
return newBid;
}
app.post("/bid", async (req, res) => {
try {
const { auctionId, user, amount } = req.body;
const bid = await placeBid(auctionId, user, amount);
res.json({ success: true, bid });
} catch (err) {
res.status(400).json({ error: err.message });
}
});
io.on("connection", (socket) => {
socket.on("joinAuction", (auctionId) => {
socket.join(auctionId);
});
});
server.listen(3000, () => {
console.log("Server running on port 3000");
});
</script>
<script type="module">
import express from "express";
import fetch from "node-fetch";
import Database from "@replit/database";
const app = express();
app.use(express.json());
const db = new Database();
// Example usecase: integrating external payment API for winning bid checkout
app.post("/checkout", async (req, res) => {
try {
const { auctionId, userId } = req.body;
const auction = await db.get(`auction:${auctionId}`);
if (!auction) throw new Error("Auction not found");
const winningBid = auction.bids?.[auction.bids.length - 1];
if (!winningBid || winningBid.user !== userId) throw new Error("Not authorized");
// example external payment provider API (mock or replace with real API key)
const paymentReq = await fetch("https://api.sandbox.paymentservice.com/create\_payment", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.PAYMENT_API_KEY}`
},
body: JSON.stringify({
amount: winningBid.amount,
currency: "USD",
description: `Payment for auction item: ${auction.item}`
})
});
const paymentRes = await paymentReq.json();
if (!paymentReq.ok) throw new Error(paymentRes.message || "Payment failed");
await db.set(`payment:${auctionId}`, { userId, paymentId: paymentRes.id, status: "initiated" });
res.json({ success: true, payment: paymentRes });
} catch (err) {
res.status(400).json({ error: err.message });
}
});
app.get("/payment-status/:auctionId", async (req, res) => {
const { auctionId } = req.params;
const paymentRecord = await db.get(`payment:${auctionId}`);
res.json(paymentRecord || { error: "No payment found" });
});
app.listen(3000, () => console.log("Auction checkout API running on port 3000"));
</script>
<script type="module">
import express from "express";
import Database from "@replit/database";
import cron from "node-cron";
const app = express();
app.use(express.json());
const db = new Database();
// Usecase: automatically close auctions past their end time and mark winners
async function closeExpiredAuctions() {
const keys = await db.list("auction:");
const now = Date.now();
for (const key of keys) {
const auction = await db.get(key);
if (!auction || auction.closed) continue;
if (now > auction.endsAt) {
const highestBid = auction.bids?.[auction.bids.length - 1];
auction.closed = true;
auction.winner = highestBid ? highestBid.user : null;
await db.set(key, auction);
console.log(`Closed auction ${key} - winner: ${auction.winner || "none"}`);
}
}
}
// run every minute, adjust frequency as needed
cron.schedule("_ _ _ _ \*", closeExpiredAuctions);
app.get("/auctions/active", async (req, res) => {
const keys = await db.list("auction:");
const auctions = await Promise.all(keys.map(k => db.get(k)));
const active = auctions.filter(a => a && !a.closed);
res.json(active);
});
app.get("/auctions/closed", async (req, res) => {
const keys = await db.list("auction:");
const auctions = await Promise.all(keys.map(k => db.get(k)));
const closed = auctions.filter(a => a && a.closed);
res.json(closed);
});
app.listen(3000, () => console.log("Auction monitor running on port 3000"));
</script>

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Replit works very well for quickly building and running web apps — including a small-to-medium auction platform — but only if you structure it the right way: isolate backend logic in an Express.js (Node.js) server, manage your database connections and secrets correctly using Replit Secrets, and separate frontend code (like React or basic HTML pages). For persistent data, use an external hosted database (like MongoDB Atlas or Supabase) instead of relying on Replit’s filesystem. Run your server through a dedicated index.js, handle your API routes in a routes/ folder, and make sure to keep your environment variables stored safely. Avoid using Replit for heavy file storage or long-running background jobs — it’s great for request/response-based apps and live collaboration, not for high-scale production loads.
Here’s a structure that works really well inside Replit:
auction-platform/
│
├── index.js // Your main server entry
├── package.json
├── routes/
│ ├── items.js // For auction items
│ ├── bids.js // For bids management
│
├── models/
│ ├── Item.js // MongoDB schema for auction item
│ ├── Bid.js // MongoDB schema for bids
│
├── public/
│ ├── index.html // Basic frontend UI
│ ├── script.js
│
└── .replit // Controls how Replit runs your code
This file initializes Express, connects to your DB, and mounts your routes. In your Replit folder root, create a file called index.js and paste this:
import express from "express"
import mongoose from "mongoose"
import dotenv from "dotenv"
import itemsRouter from "./routes/items.js"
import bidsRouter from "./routes/bids.js"
dotenv.config() // Loads secrets from Replit environment
const app = express()
app.use(express.json())
// Connect to MongoDB using Replit Secrets panel variable: MONGO_URI
mongoose.connect(process.env.MONGO_URI)
.then(() => console.log("✅ Connected to MongoDB"))
.catch(err => console.error("Mongo connection error:", err))
// Mount your routes
app.use("/api/items", itemsRouter)
app.use("/api/bids", bidsRouter)
// Health check route
app.get("/", (req, res) => {
res.send("Auction platform backend is running!")
})
// Replit automatically picks up this port
app.listen(3000, () => console.log("🚀 Server running on port 3000"))
Open the Replit sidebar → Lock icon called “Secrets” → add a new key named MONGO\_URI with your connection string from MongoDB Atlas (e.g. mongodb+srv://user:pass@cluster...). Never hardcode this in your code!
Each file under the routes/ directory defines how your API handles data. Create routes/items.js with this:
import express from "express"
import Item from "../models/Item.js"
const router = express.Router()
// Create item route (auction listing)
router.post("/", async (req, res) => {
try {
const { title, description, startingPrice } = req.body
const newItem = new Item({ title, description, startingPrice })
await newItem.save()
res.json(newItem)
} catch (err) {
res.status(500).json({ error: "Unable to create item" })
}
})
// Get all items
router.get("/", async (req, res) => {
const items = await Item.find()
res.json(items)
})
export default router
Models define how data looks in MongoDB. Create models/Item.js and connect it with Mongoose:
import mongoose from "mongoose"
const itemSchema = new mongoose.Schema({
title: String,
description: String,
startingPrice: Number,
createdAt: { type: Date, default: Date.now }
})
export default mongoose.model("Item", itemSchema)
For a very simple test UI, create a public/index.html file:
<!DOCTYPE html>
<html>
<head>
<title>Auction Platform</title>
</head>
<body>
<h1>Welcome to Replit Auction</h1>
<form id="itemForm">
<input type="text" placeholder="Title" id="title" required />
<input type="text" placeholder="Description" id="description" />
<input type="number" placeholder="Starting Price" id="price" required />
<button type="submit">Create Auction Item</button>
</form>
<ul id="itemsList"></ul>
<script src="/script.js"></script>
</body>
</html>
Then create public/script.js:
const form = document.getElementById("itemForm")
form.addEventListener("submit", async (e) => {
e.preventDefault()
const newItem = {
title: document.getElementById("title").value,
description: document.getElementById("description").value,
startingPrice: parseFloat(document.getElementById("price").value)
}
const res = await fetch("/api/items", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify(newItem)
})
const data = await res.json()
alert("Created item: " + data.title)
})
run = "npm start"
"scripts": {
"start": "node index.js"
}
A successful auction platform on Replit means: a clear Node backend (Express), database hosted externally (MongoDB or Supabase), correct use of Replit Secrets, and lightweight frontend inside public/. Keep everything modular (routes, models, static files) so teammates can easily debug or extend it. Replit is amazing for live collaboration and demos, and with a few guardrails, it’s solid for small functional projects — just don’t treat it like a persistent production host.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.