Learn how to build an inventory system using Replit step by step. Perfect guide for beginners to manage products efficiently online.

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 inventory system on Replit, you can use a simple Node.js + Express backend with a small JSON-based storage or Replit’s built-in Database if you want persistent data across restarts. You’ll have one file for the backend (like index.js), an optional frontend (like public/index.html) if you want a simple interface, and a few helper modules (like db.js to manage your data). In Replit, this setup will run completely in the browser environment and auto-restart on changes. You’ll be able to add, view, edit, and delete inventory items by sending requests from the frontend or using a tool like Postman.
In your Replit, start a new Repl using the Node.js template. That gives you an index.js file automatically.
Open the Shell tab (bottom or right side in Replit) and install Express:
npm install express
If you want to use Replit’s built-in database, install it too:
npm install @replit/database
Inside index.js, create a simple Express app. This file will act as your backend API that manages inventory items.
// index.js
const express = require("express");
const app = express();
app.use(express.json()); // Needed to read JSON from requests
const PORT = process.env.PORT || 3000;
// For persistent storage, we use Replit DB
const Database = require("@replit/database");
const db = new Database();
// Define a basic route to test the server
app.get("/", (req, res) => {
res.send("Inventory API is running");
});
Add the following routes below your test route inside index.js. These control how inventory data is created, fetched, updated, and deleted.
// POST /items - Add a new item
app.post("/items", async (req, res) => {
const { name, quantity } = req.body;
if (!name || quantity === undefined) {
return res.status(400).send("Missing name or quantity");
}
await db.set(name, { name, quantity });
res.send({ message: "Item added", item: { name, quantity } });
});
// GET /items - View all items
app.get("/items", async (req, res) => {
const keys = await db.list();
const allItems = [];
for (const key of keys) {
const item = await db.get(key);
allItems.push(item);
}
res.send(allItems);
});
// PUT /items/:name - Update existing item
app.put("/items/:name", async (req, res) => {
const name = req.params.name;
const existing = await db.get(name);
if (!existing) return res.status(404).send("Item not found");
const updated = { ...existing, ...req.body };
await db.set(name, updated);
res.send({ message: "Item updated", item: updated });
});
// DELETE /items/:name - Remove an item
app.delete("/items/:name", async (req, res) => {
const name = req.params.name;
await db.delete(name);
res.send({ message: "Item deleted" });
});
Finally, at the very bottom of index.js, start the server:
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Create a new folder named public in your Repl, and add a file inside it named index.html. This will become your minimal interface to interact with the inventory.
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Inventory Manager</title>
</head>
<body>
<h1>Inventory</h1>
<form id="addForm">
<input type="text" id="name" placeholder="Item name" required />
<input type="number" id="quantity" placeholder="Quantity" required />
<button type="submit">Add Item</button>
</form>
<ul id="inventory"></ul>
<script>
const form = document.getElementById("addForm");
const inventoryList = document.getElementById("inventory");
async function loadItems() {
const res = await fetch("/items");
const items = await res.json();
inventoryList.innerHTML = "";
items.forEach((item) => {
const li = document.createElement("li");
li.textContent = `${item.name} - ${item.quantity}`;
inventoryList.appendChild(li);
});
}
form.addEventListener("submit", async (e) => {
e.preventDefault();
const name = document.getElementById("name").value;
const quantity = document.getElementById("quantity").value;
await fetch("/items", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, quantity }),
});
form.reset();
loadItems();
});
loadItems();
</script>
</body>
</html>
In your index.js, tell Express to serve this folder. Add this line above your routes:
app.use(express.static("public"));
This is a fully working small inventory system inside Replit — no external setup required. You can grow this setup by adding authentication, connecting to a proper database, or styling your frontend later, but this version demonstrates all the important mechanics clearly and correctly for a real Replit environment.
const express = require('express');
const bodyParser = require('body-parser');
const Database = require('@replit/database');
const app = express();
const db = new Database();
app.use(bodyParser.json());
// Update or create an inventory item with quantity adjustment
app.post('/api/inventory/update', async (req, res) => {
const { sku, deltaQty, name } = req.body;
if (!sku || !deltaQty) return res.status(400).json({ error: 'Missing sku or deltaQty' });
try {
const existing = await db.get(sku) || { name: name || 'Unnamed Item', qty: 0 };
const newQty = Math.max(0, existing.qty + deltaQty);
await db.set(sku, { ...existing, qty: newQty });
res.json({ sku, ...existing, qty: newQty });
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// Fetch all inventory items
app.get('/api/inventory', async (req, res) => {
try {
const allItems = await db.list();
const data = {};
for (const key of allItems) {
data[key] = await db.get(key);
}
res.json(data);
} catch (e) {
res.status(500).json({ error: e.message });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log('Inventory API running on port', PORT));
import express from "express";
import fetch from "node-fetch";
import Database from "@replit/database";
const app = express();
const db = new Database();
app.use(express.json());
// Sync local inventory item with an external supplier API
app.post("/api/inventory/syncSupplier", async (req, res) => {
const { sku } = req.body;
if (!sku) return res.status(400).json({ error: "Missing SKU" });
try {
const localItem = await db.get(sku);
if (!localItem) return res.status(404).json({ error: "Item not found" });
const supplierResponse = await fetch(
`https://api.supplier.example.com/products/${sku}`,
{
headers: { Authorization: `Bearer ${process.env.SUPPLIER_API_KEY}` },
}
);
if (!supplierResponse.ok)
throw new Error(`Supplier API error: ${supplierResponse.status}`);
const supplierData = await supplierResponse.json();
const updatedItem = {
...localItem,
supplierPrice: supplierData.price,
supplierStock: supplierData.stock,
lastSynced: new Date().toISOString(),
};
await db.set(sku, updatedItem);
res.json({ message: "Synced successfully", item: updatedItem });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log("Sync API running on port", PORT));
import express from "express";
import Database from "@replit/database";
const app = express();
const db = new Database();
app.use(express.json());
// Atomic inventory reservation for concurrent checkout requests
app.post("/api/inventory/reserve", async (req, res) => {
const { sku, quantity } = req.body;
if (!sku || !quantity) return res.status(400).json({ error: "Missing sku or quantity" });
try {
const lockKey = `lock_${sku}`;
const lock = await db.get(lockKey);
if (lock) return res.status(423).json({ error: "Item is being updated, try again." });
await db.set(lockKey, true);
const item = (await db.get(sku)) || { qty: 0 };
if (item.qty < quantity) {
await db.delete(lockKey);
return res.status(409).json({ error: "Not enough stock" });
}
item.qty -= quantity;
await db.set(sku, item);
await db.delete(lockKey);
return res.json({ reserved: quantity, remaining: item.qty });
} catch (err) {
await db.delete(`lock_${req.body.sku}`);
res.status(500).json({ error: err.message });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log("Inventory Reservation API running on port", PORT));

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Building an inventory system on Replit works best if you treat it like a small cloud-hosted development environment — lightweight, persistent while it’s active, but not meant to store tons of data long-term or run 24/7 without external help. The simplest robust structure is a Node.js + Express backend with a SQLite or external database, a simple HTML/JS frontend, and environment variables handled as Replit Secrets. You’ll use the “Files” pane to create folders and files for routes, UI, and configuration. The key is: separate your logic in folders, use Replit secrets for credentials, don’t rely on local JSON files for production data, and test endpoints directly inside Replit’s built-in webview or a browser.
Here’s how your Replit file tree might look:
routes/inventory.js)
In Replit, create a new Node.js repl. Replace the auto-created index.js with this:
// index.js
import express from "express"
import bodyParser from "body-parser"
import cors from "cors"
import inventoryRoutes from "./routes/inventory.js"
const app = express()
app.use(cors())
app.use(bodyParser.json())
// Mount your routes
app.use("/api/inventory", inventoryRoutes)
// Static frontend (if any)
app.use(express.static("public"))
const PORT = process.env.PORT || 3000
app.listen(PORT, () => console.log(`Server running on port ${PORT}`))
Run this once using the “Run” button. You’ll see “Server running on port …” in the console. Replit automatically creates a public URL at the top of your screen where your app is live while active.
You can use SQLite (simple, built-in file-based database that works well inside Replit). In your db.js file:
// db.js
import Database from "better-sqlite3"
// Create a local database file named 'inventory.db' inside project root
const db = new Database("inventory.db")
// Create table if it doesn't exist
db.prepare(`
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
quantity INTEGER NOT NULL,
price REAL NOT NULL
);
`).run()
export default db
Replit will persist this inventory.db file as long as your repl exists. Don’t rely on it for mission-critical long-term data, but it’s perfect for small projects or prototypes.
Under a new folder routes (create it using Replit’s file tree UI), create inventory.js:
// routes/inventory.js
import express from "express"
import db from "../db.js"
const router = express.Router()
// GET all items
router.get("/", (req, res) => {
const items = db.prepare("SELECT * FROM items").all()
res.json(items)
})
// POST to add new item
router.post("/", (req, res) => {
const { name, quantity, price } = req.body
const stmt = db.prepare("INSERT INTO items (name, quantity, price) VALUES (?, ?, ?)")
const result = stmt.run(name, quantity, price)
res.json({ id: result.lastInsertRowid, name, quantity, price })
})
// PUT to update existing item
router.put("/:id", (req, res) => {
const { id } = req.params
const { name, quantity, price } = req.body
db.prepare("UPDATE items SET name=?, quantity=?, price=? WHERE id=?").run(name, quantity, price, id)
res.json({ message: "Item updated" })
})
// DELETE an item
router.delete("/:id", (req, res) => {
const { id } = req.params
db.prepare("DELETE FROM items WHERE id=?").run(id)
res.json({ message: "Item deleted" })
})
export default router
Inside your public folder, create index.html with a small UI to test the API. It’ll be served automatically because of express.static("public") in your index.js.
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Inventory System</title>
</head>
<body>
<h1>My Inventory</h1>
<button id="load">Load Items</button>
<ul id="list"></ul>
<script>
document.getElementById("load").onclick = async () => {
const res = await fetch("/api/inventory")
const items = await res.json()
const list = document.getElementById("list")
list.innerHTML = ""
items.forEach(i => {
const li = document.createElement("li")
li.textContent = `${i.name} (${i.quantity}) - $${i.price}`
list.appendChild(li)
})
}
</script>
</body>
</html>
Now, press “Run”, and your Replit preview window will show a simple working inventory fetcher connected to your live backend.
Go to the Secrets tab on left sidebar (key icon). Add variables like DB_URL or API_KEY. In your code, you can access them with process.env.DB\_URL. Never hardcode credentials — Replit keeps them safe this way.
For teams, turn on Replit’s Multiplayer so others can edit with you in real-time. Connect a GitHub repo from the version control panel (the ‘branch’ icon) so you can commit and push changes. Avoid committing inventory.db if data is sensitive – add it to .gitignore.
Your inventory system on Replit should have a clear folder structure, SQLite for lightweight persistence, Express routes for CRUD operations, and Replit secrets for any private credentials. Test via Replit webview, commit changes via built-in Git integration, and if you scale up, you can later connect to an external database and upgrade to Replit Deployments for uptime. This setup is reliable, fast to build, and fully aligns with how Replit’s environment really works.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.